diff --git a/.github/scripts/validate_bat.py b/.github/scripts/validate_bat.py index be55823..7ae860c 100644 --- a/.github/scripts/validate_bat.py +++ b/.github/scripts/validate_bat.py @@ -115,6 +115,11 @@ def get_active_lines(content): return result +def _reg_key_normalize(raw: str) -> str: + """Normalise un chemin de registre : minuscules, guillemets retirés, séparateurs consolidés.""" + return raw.strip().strip('"').lower() + + # ─── Logique de détection codée en dur (pas des règles métier) ──────────────── # Ces éléments sont des patterns de détection — ils ne peuvent pas être exprimés # sous forme de simples valeurs dans un tableau markdown. @@ -1162,6 +1167,129 @@ def test_advertising_id_disabled(content): return errors +def test_no_duplicate_commands(active_lines): + """ + Test 62 — Vérifie qu'aucune commande n'est dupliquée dans win11-setup.bat. + + Précision maximale — catégories analysées (insensibles à la casse) : + [REG] reg add / reg delete — (opération, chemin clé, nom valeur) + [SC] sc stop / config / failure — (opération, nom service) + [TASK] schtasks /Change /TN — nom de tâche normalisé + [HOSTS] echo 0.0.0.0 / 127.0.0.1 — domaine bloqué + [DEL] del — chemin de fichier normalisé + + Exclusions légitimes : + - Lignes commençant par echo (log/hosts write) : ignorées pour reg/sc/del + - Variables de boucle batch (%%X, %X%) : ignorées pour sc + - Même TN sur /Query && /Change sur la même ligne : comptée une seule fois + """ + reg_seen = {} # (op, key, val) -> [linenos] + sc_seen = {} # (op, svc) -> [linenos] + task_seen = {} # tn_low -> [linenos] + hosts_seen = {} # domain_low -> [linenos] + del_seen = {} # path_low -> [linenos] + + for lineno, line in active_lines: + stripped = line.strip() + + # Lignes echo : exclues pour reg/sc/del (mais capturées pour hosts) + is_echo_line = bool(re.match(r"^echo\b", stripped, re.IGNORECASE)) + + # ── 1. reg add / reg delete ────────────────────────────────────────── + if not is_echo_line and re.search(r"\breg\s+(?:add|delete)\b", stripped, re.IGNORECASE): + m_op = re.search(r"\breg\s+(add|delete)\b", stripped, re.IGNORECASE) + op = m_op.group(1).lower() + + # Chemin de clé (guillemets ou non) + m_key = re.search(r"\breg\s+(?:add|delete)\s+\"([^\"]+)\"", stripped, re.IGNORECASE) + if not m_key: + m_key = re.search( + r"\breg\s+(?:add|delete)\s+([^\s\"\'\/][^\s]*)", stripped, re.IGNORECASE + ) + if not m_key: + continue + key = _reg_key_normalize(m_key.group(1)) + + # Nom de valeur : /ve = valeur par défaut, /v NOM (avec ou sans guillemets) + if re.search(r"(?:^|\s)/ve\b", stripped, re.IGNORECASE): + val = "(default)" + else: + m_val = re.search(r"/v\s+\"([^\"]+)\"", stripped, re.IGNORECASE) + if not m_val: + m_val = re.search(r"/v\s+(\S+)", stripped, re.IGNORECASE) + val = m_val.group(1).lower() if m_val else "(default)" + + reg_seen.setdefault((op, key, val), []).append(lineno) + + # ── 2. sc stop / config / failure ──────────────────────────────────── + if not is_echo_line and re.search( + r"\bsc\s+(?:stop|config|failure)\b", stripped, re.IGNORECASE + ): + m = re.search(r"\bsc\s+(stop|config|failure)\s+(\S+)", stripped, re.IGNORECASE) + if m: + op_sc = m.group(1).lower() + svc = m.group(2).lower() + # Exclure les variables de boucle batch (%%VAR ou %VAR%) + if not svc.startswith("%"): + sc_seen.setdefault((op_sc, svc), []).append(lineno) + + # ── 3. schtasks /Change (/Query && /Change sur la même ligne) ───────── + if not is_echo_line and re.search(r"\bschtasks\b", stripped, re.IGNORECASE) \ + and re.search(r"/Change\b", stripped, re.IGNORECASE): + # Collecter les TN uniques sur cette ligne (set → évite double-comptage + # lorsque /Query et /Change partagent le même /TN sur une seule ligne) + tns_on_line = set() + for m in re.finditer(r"/TN\s+\"([^\"]+)\"", stripped, re.IGNORECASE): + tns_on_line.add(m.group(1).lower()) + if not tns_on_line: + # Fallback : TN sans guillemets + for m in re.finditer(r"/TN\s+([^\s\/][^\s]*)", stripped, re.IGNORECASE): + tns_on_line.add(m.group(1).lower()) + for tn in tns_on_line: + task_seen.setdefault(tn, []).append(lineno) + + # ── 4. Entrées hosts (echo 0.0.0.0 / 127.0.0.1 DOMAIN) ────────────── + # Capturées sur les lignes echo (ajout dynamique dans le script) + m_hosts = re.search( + r"\becho\s+(?:0\.0\.0\.0|127\.0\.0\.1)\s+([^\s\>\|\&\"\']+)", + stripped, re.IGNORECASE + ) + if m_hosts: + domain = m_hosts.group(1).lower() + hosts_seen.setdefault(domain, []).append(lineno) + + # ── 5. del (suppression de fichiers) ───────────────────────────────── + if not is_echo_line and re.search(r"\bdel\b", stripped, re.IGNORECASE): + # Chemin entre guillemets + m_del = re.search(r"\bdel\b[^\"]*\"([^\"]+)\"", stripped, re.IGNORECASE) + if not m_del: + # Chemin sans guillemets : del /f /q /s path + m_del = re.search( + r"\bdel\b(?:\s+/\w+)*\s+([^\s\>\|\&\"\']+)", stripped, re.IGNORECASE + ) + if m_del: + path = m_del.group(1).lower() + del_seen.setdefault(path, []).append(lineno) + + errors = [] + + def _report(seen, category, label_fn): + for sig, linenos in sorted(seen.items(), key=lambda x: x[1][0]): + if len(linenos) > 1: + nums = ", ".join(str(n) for n in linenos) + errors.append( + f" [{category}] doublon ligne(s) {nums} — {label_fn(sig)}" + ) + + _report(reg_seen, "REG", lambda s: f'reg {s[0]} "{s[1]}" /v {s[2]}') + _report(sc_seen, "SC", lambda s: f"sc {s[0]} {s[1]}") + _report(task_seen, "TASK", lambda s: f'/TN "{s}"') + _report(hosts_seen, "HOSTS", lambda s: f"0.0.0.0 {s}") + _report(del_seen, "DEL", lambda s: f'del "{s}"') + + return errors + + def write_github_summary(tests_results, passed, failed): """Écrit un rapport markdown dans $GITHUB_STEP_SUMMARY si disponible.""" import os @@ -1351,6 +1479,9 @@ def main(): test_sfc_dism_section19b(content)), ("61 Advertising ID desactive (AdvertisingInfo DisabledByGroupPolicy=1)", test_advertising_id_disabled(content)), + # ── Test 62 — Doublons de commandes ─────────────────────────────────── + ("62 Aucune commande en doublon (reg add/del, sc stop/config/failure, schtasks, hosts, del)", + test_no_duplicate_commands(active_lines)), ] passed = 0 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 7f3e58e..af12868 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -9,7 +9,7 @@ on: jobs: validate: - name: "🔍 59 vérifications automatiques" + name: "🔍 60 vérifications automatiques" runs-on: ubuntu-latest steps: @@ -21,7 +21,7 @@ jobs: with: python-version: "3.x" - - name: "🧪 Validation statique (59 tests)" + - name: "🧪 Validation statique (60 tests)" run: | echo "## 🚀 Démarrage de la validation..." >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY diff --git a/win11-setup.bat b/win11-setup.bat index 2671cb4..7db3046 100644 --- a/win11-setup.bat +++ b/win11-setup.bat @@ -169,16 +169,11 @@ reg add "HKCU\SOFTWARE\Microsoft\AppSettings" /v Skype-UserConsentAccepted /t RE reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\UserProfileEngagement" /v AccountNotifications /t REG_DWORD /d 0 /f >nul 2>&1 :: Appels téléphoniques — accès apps UWP refusé reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\phoneCall" /v Value /t REG_SZ /d Deny /f >nul 2>&1 -:: Recherche cloud désactivée -reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v AllowCloudSearch /t REG_DWORD /d 0 /f >nul 2>&1 :: OneDrive — policy non écrite (conservé, démarrage géré par l'utilisateur) :: Event Transcript — désactiver la base de données locale des événements télémétrie (réduit I/O disque) reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Diagnostics\DiagTrack\EventTranscriptKey" /v EnableEventTranscript /t REG_DWORD /d 0 /f >nul 2>&1 :: MRT — ne pas remonter les résultats d'analyse d'infection au cloud Microsoft reg add "HKLM\SOFTWARE\Policies\Microsoft\MRT" /v DontReportInfectionInformation /t REG_DWORD /d 1 /f >nul 2>&1 -:: Tailored Experiences — désactiver les recommandations personnalisées basées sur les données de diagnostic (HKLM) -reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\CloudContent" /v DisableTailoredExperiencesWithDiagnosticData /t REG_DWORD /d 1 /f >nul 2>&1 - :: Desktop Analytics — désactiver le traitement des données analytiques locales reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\DataCollection" /v EnableDesktopAnalyticsProcessing /t REG_DWORD /d 0 /f >nul 2>&1 echo [%date% %time%] Section 6 : Telemetrie/AI/Copilot/Recall/SIUF/CEIP/Defender/DataCollection OK >> "%LOG%" @@ -450,7 +445,6 @@ reg add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" /v SMB1 :: Note: DisableWindowsSpotlightFeatures interdit (prerequis_WIN11.md — Spotlight conservé) reg add "HKCU\SOFTWARE\Policies\Microsoft\Windows\CloudContent" /v DisableWindowsSpotlightOnActionCenter /t REG_DWORD /d 1 /f >nul 2>&1 reg add "HKCU\SOFTWARE\Policies\Microsoft\Windows\CloudContent" /v DisableWindowsSpotlightOnSettings /t REG_DWORD /d 1 /f >nul 2>&1 -reg add "HKCU\SOFTWARE\Policies\Microsoft\Windows\CloudContent" /v DisableTailoredExperiencesWithDiagnosticData /t REG_DWORD /d 1 /f >nul 2>&1 :: Notifications sync provider — désactiver les pubs OneDrive/tiers dans l'Explorateur reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v ShowSyncProviderNotifications /t REG_DWORD /d 0 /f >nul 2>&1