#!/usr/bin/env bash # scan.sh — recherche les fichiers de configuration et éléments sensibles # accidentellement exposés sur la preprod. # # USAGE # ./scan.sh [targets.env] # # Vérifie la présence et la lisibilité de chemins courants : # - dépôts git exposés (/.git/...) # - fichiers d'env / settings (.env, appsettings.json, web.config) # - surfaces de doc API (swagger, openapi) # - répertoires de listing # # Journalise dans $OUTPUT_DIR/config-scan.log. Signale tout ce qui # répond 200 (ou 403 = présent mais protégé) pour un passage en revue. COMMON_SH="$(cd "$(dirname "$0")/../.." && pwd)/lib/common.sh" # shellcheck disable=SC1091 source "$COMMON_SH" load_target "${1:-}" mkdir -p "${OUTPUT_DIR:-./output}" LOG="${OUTPUT_DIR:-./output}/config-scan.log" : > "$LOG" # Chemins à tester (relatifs à TARGET_BASE_URL). Pas de cible tierce. PATHS=( "/.git/HEAD" "/.git/config" "/.env" "/.env.local" "/appsettings.json" "/appsettings.Development.json" "/appsettings.Production.json" "/web.config" "/connectionstrings.json" "/swagger" "/swagger/v1/swagger.json" "/swagger/index.html" "/openapi.json" "/openapi/v1.json" "/robots.txt" "/.well-known/openid-configuration" "/elmah.axd" "/trace.axd" "/wp-config.php" "/server-status" "/.DS_Store" "/backup" "/.vscode" ) info "Scan de ${#PATHS[@]} chemins sur ${#AUTHORIZED_HOSTS[@]} hôte(s) du périmètre" info "Journal : $LOG" # Endpoints **intentionnellement publics** (un 200 est normal, pas une # fuite) : la discovery OIDC d'IdentityServer, requise par les clients # (PostIt) pour se connecter. EXPECTED_PUBLIC=( "/.well-known/openid-configuration" "/.well-known/jwks" ) is_expected_public() { local p="$1" local e for e in "${EXPECTED_PUBLIC[@]}"; do [[ "$e" == "$p" ]] && return 0; done return 1 } while IFS= read -r base; do info "→ $base" for p in "${PATHS[@]}"; do url="${base}${p}" # http_probe gère déjà assert_in_scope. http_probe GET "$url" code="$LAST_CODE" size=0 [[ -n "$LAST_BODY" && -s "$LAST_BODY" ]] && size=$(wc -c < "$LAST_BODY") printf '%s\t%s\t%s\t%8d\t%s\n' "$(date -Is)" "$base" "$code" "$size" "$p" >> "$LOG" case "$code" in 200) if is_expected_public "$p"; then ok "200 $base$p (${size} o) — public attendu (OIDC discovery)" else ok "200 $base$p (${size} o) — EXPOSÉ, à corriger" fi ;; 401|403) warn "$code $base$p — présent mais protégé" ;; 301|302) warn "$code $base$p — redirection (vérifier la cible)" ;; 404) ;; *) warn "$code $base$p" ;; esac drop_body pace done done < <(each_scope_url) info "Terminé. Voir $LOG."