import re, json, sys, io from openpyxl import load_workbook from unidecode import unidecode sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') ps = open(r'D:\VNPT-M\Tool\audit_tool\NEW\Update\audit_lhdsin\audit_check_script\audit_cis_windows.ps1', 'r', encoding='utf-8').read() # Collect ALL criteria including DC/MS variants all_criteria = [] # Direct Write-CheckResult calls for m in re.finditer(r'Write-CheckResult\s+"([^"]+)"\s+"([^"]+)"', ps): check_id = m.group(1) desc = m.group(2) if not check_id.startswith('$'): all_criteria.append((check_id, desc)) # Expand variable-based calls fw_calls = re.findall(r'Test-FirewallProfile\s+"(\w+)"\s+"([\d.]+)"\s+"(\w+)"', ps) for profile, prefix, label in fw_calls: all_criteria.append((f'{prefix}.1', f"Thiet lap trang thai 'Windows Firewall: {label} : Firewall state'")) all_criteria.append((f'{prefix}.2', f"Thiet lap trang thai 'Windows Firewall: {label} : Inbound connections'")) all_criteria.append((f'{prefix}.3', f"Thiet lap trang thai 'Windows Firewall: {label} : Outbound connections'")) log_calls = re.findall(r'Test-EventLogPolicy\s+"(\w+)"\s+"([\d.]+)"\s+"(\w+)"', ps) for logname, prefix, label in log_calls: all_criteria.append((f'{prefix}.1', f"Thiet lap chinh sach '{label} : Control Event Log behavior when the log file reaches its maximum size'")) all_criteria.append((f'{prefix}.2', f"Thiet lap chinh sach '{label} : Specify the maximum log file size (KB)'")) # Build full criteria names, keep duplicates (DC vs MS variants) criteria_list = [] seen = set() for cid, desc in all_criteria: full = f"{cid}. {desc}" key = (cid, unidecode(desc)) if key not in seen: seen.add(key) criteria_list.append((cid, desc, full)) print(f'Total criteria (with variants): {len(criteria_list)}') # Load Excel wb = load_workbook(r'D:\VNPT-M\Tool\audit_tool\NEW\Update\audit_lhdsin\config\Windows_Checklist.xlsx') ws = wb.worksheets[0] # Build Excel row mapping: try to find the best row for each criteria # A row can have multiple criteria (DC + MS variants) row_to_criteria = {} # row -> list of criteria full text unmatched = list(criteria_list) # Pass 1: match by full text for row in range(1, ws.max_row + 1): val_a = str(ws.cell(row=row, column=1).value or "").strip() val_b = str(ws.cell(row=row, column=2).value or "").strip() combined = f"{val_a} {val_b}".strip() norm = unidecode(combined) for cid, desc, full in list(unmatched): if unidecode(full) == norm: if row not in row_to_criteria: row_to_criteria[row] = [] row_to_criteria[row].append(full) unmatched.remove((cid, desc, full)) # Pass 2: match by CheckId in column A for cid, desc, full in list(unmatched): for row in range(1, ws.max_row + 1): val_a = str(ws.cell(row=row, column=1).value or "").strip() if val_a and cid in val_a: if row not in row_to_criteria: row_to_criteria[row] = [] if full not in row_to_criteria[row]: row_to_criteria[row].append(full) unmatched.remove((cid, desc, full)) break # Pass 3: match by CheckId in column B for cid, desc, full in list(unmatched): for row in range(1, ws.max_row + 1): if row in row_to_criteria: continue val_b = str(ws.cell(row=row, column=2).value or "").strip() if val_b: norm_b = unidecode(val_b) parts = norm_b.split() if parts and cid == parts[0].rstrip('.'): if row not in row_to_criteria: row_to_criteria[row] = [] if full not in row_to_criteria[row]: row_to_criteria[row].append(full) unmatched.remove((cid, desc, full)) break print(f'\nMatched rows: {len(row_to_criteria)}') print(f'Unmatched criteria: {len(unmatched)}') if unmatched: print('\nUnmatched:') for cid, desc, full in unmatched[:10]: print(f' {cid}: {full[:80]}') # Build new config new_data = [] for row in sorted(row_to_criteria.keys()): for full_text in row_to_criteria[row]: new_data.append({str(row): full_text}) new_config = {"data": new_data} output_path = r'D:\VNPT-M\Tool\audit_tool\NEW\Update\audit_lhdsin\config\windows_config.json' with open(output_path, 'w', encoding='utf-8') as f: json.dump(new_config, f, ensure_ascii=False, indent=4) print(f'\nNew config: {output_path}') print(f'Total entries: {len(new_data)}') # Verify against actual output out_file = r'D:\VNPT-M\Tool\audit_tool\NEW\Update\audit_lhdsin\output\MEVAS-NTL-DATAN_2026_07_29-05_18_36.txt' with open(out_file, 'r', encoding='utf-8', errors='replace') as f: lines = f.read().splitlines() audit_results = {} for line in lines: line = line.strip() if line.startswith('{') and line.endswith('}'): try: parsed = json.loads(line) key = unidecode(str(list(parsed.keys())[0])) audit_results[key] = (str(list(parsed.values())[0]), line) except: pass cfg_names = set() for item in new_config['data']: cfg_names.add(unidecode(str(list(item.values())[0]))) output_names = set(audit_results.keys()) matched = cfg_names & output_names output_only = output_names - cfg_names print(f'Output lines: {len(output_names)}') print(f'Matched: {len(matched)}/{len(output_names)}') print(f'Not in config: {len(output_only)}') if output_only: print('Still missing (first 5):') for n in sorted(list(output_only))[:5]: print(f' {n[:80]}')