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() # 1. Static checks static_checks = re.findall(r'Write-CheckResult\s+"([^$"].+?)"\s+"(.+?)"', ps) criteria = [(cid, desc) for cid, desc in static_checks] print(f'Static checks: {len(criteria)}') # 2. Variable-based checks - expand from function calls # Test-FirewallProfile fw_calls = re.findall(r'Test-FirewallProfile\s+"(\w+)"\s+"([\d.]+)"\s+"(\w+)"', ps) for profile, prefix, label in fw_calls: criteria.append((f'{prefix}.1', f"Thiet lap trang thai 'Windows Firewall: {label} : Firewall state'")) criteria.append((f'{prefix}.2', f"Thiet lap trang thai 'Windows Firewall: {label} : Inbound connections'")) criteria.append((f'{prefix}.3', f"Thiet lap trang thai 'Windows Firewall: {label} : Outbound connections'")) # Test-EventLogPolicy log_calls = re.findall(r'Test-EventLogPolicy\s+"(\w+)"\s+"([\d.]+)"\s+"(\w+)"', ps) for logname, prefix, label in log_calls: criteria.append((f'{prefix}.1', f"Thiet lap chinh sach '{label} : Control Event Log behavior when the log file reaches its maximum size'")) criteria.append((f'{prefix}.2', f"Thiet lap chinh sach '{label} : Specify the maximum log file size (KB)'")) print(f'Total after expanding: {len(criteria)}') # Build lookup: normalized_full_text -> full_text ps_criteria = {} for cid, desc in criteria: full = f"{cid}. {desc}" key = unidecode(full) ps_criteria[key] = full # Load Excel wb = load_workbook(r'D:\VNPT-M\Tool\audit_tool\NEW\Update\audit_lhdsin\config\Windows_Checklist.xlsx') ws = wb.worksheets[0] excel_rows = {} unmatched = {} # Pass 1: match by full text (column B has the full criteria 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) if norm in ps_criteria: excel_rows[row] = ps_criteria[norm] del ps_criteria[norm] # Pass 2: match just the CheckId in column A still_unmatched = dict(ps_criteria) for cid_key, full_text in still_unmatched.items(): cid = full_text.split('. ')[0] if '. ' in full_text else full_text.split()[0] 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 and row not in excel_rows: excel_rows[row] = full_text del ps_criteria[cid_key] break # Pass 3: try matching by normalized column B only still_unmatched2 = dict(ps_criteria) for cid_key, full_text in still_unmatched2.items(): cid = full_text.split('. ')[0] norm_cid = unidecode(cid) for row in range(1, ws.max_row + 1): val_b = str(ws.cell(row=row, column=2).value or "").strip() if val_b: norm_b = unidecode(val_b) if norm_cid in norm_b.split()[0] and row not in excel_rows: excel_rows[row] = full_text del ps_criteria[cid_key] break print(f'\nMatched rows: {len(excel_rows)}') print(f'Unmatched: {len(ps_criteria)}') if ps_criteria: print('\nUnmatched criteria (first 10):') for i, (k, v) in enumerate(list(ps_criteria.items())[:10]): print(f' [{v[:70]}]') # Build new config new_data = [] for row in sorted(excel_rows.keys()): new_data.append({str(row): excel_rows[row]}) 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 written to: {output_path}') print(f'Total entries: {len(new_data)}')