Files
audit-web/tools/update_config_v3.py
2026-08-26 14:11:37 +07:00

150 lines
5.5 KiB
Python

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]}')