#!/usr/bin/env python3 """Validate no-secret Windows99 VMware Verify stdout captured from console.""" from __future__ import annotations import argparse import re import sys from pathlib import Path SCHEMA_VERSION = "windows99_console_verify_artifact_validator_v1" DEFAULT_REQUIRED_ALIASES = ("111", "188", "120", "121", "112") SENSITIVE_RE = re.compile( r"(?i)(password|passwd|secret|token|authorization|cookie|private[_ -]?key)\s*[=:]" ) def _normalize(text: str) -> list[str]: text = text.replace("\r\n", "\n").replace("\r", "\n") lines = [line.strip() for line in text.splitlines()] if "remote_verify_output_begin" in lines and "remote_verify_output_end" in lines: begin = lines.index("remote_verify_output_begin") + 1 end = lines.index("remote_verify_output_end") lines = lines[begin:end] return [line for line in lines if line] def _value_for(lines: list[str], key: str) -> str: prefix = f"{key}=" for line in lines: if line.startswith(prefix): return line.split("=", 1)[1] return "" def _line_matches(lines: list[str], pattern: str) -> bool: compiled = re.compile(pattern) return any(compiled.search(line) for line in lines) def _csv(items: list[str]) -> str: return ",".join(items) def validate(lines: list[str], required_aliases: tuple[str, ...]) -> tuple[int, list[str]]: missing_fields: list[str] = [] secret_like_lines = [line for line in lines if SENSITIVE_RE.search(line)] required_exact_keys = [ "AWOOOI_WINDOWS99_VMWARE_AUTOSTART", "MODE", "VMRUN_PRESENT", "VMWARE_AUTOSTART_CONFIG_READY", "VMWARE_AUTOSTART_POWER_READY", "WINDOWS_UPDATE_NO_AUTO_REBOOT_READY", "VMWARE_AUTOSTART_VERIFY_READY", ] for key in required_exact_keys: if not _value_for(lines, key): missing_fields.append(key) if _value_for(lines, "AWOOOI_WINDOWS99_VMWARE_AUTOSTART") != "1": missing_fields.append("AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1") if _value_for(lines, "MODE") != "Verify": missing_fields.append("MODE=Verify") if not any(line.startswith("VMWARE_SERVICE ") for line in lines): missing_fields.append("VMWARE_SERVICE") if not any(line.startswith("VMWARE_AUTOSTART_TASK ") for line in lines): missing_fields.append("VMWARE_AUTOSTART_TASK") if not any(line.startswith("WINDOWS_UPDATE_POLICY ") for line in lines): missing_fields.append("WINDOWS_UPDATE_POLICY") missing_vmx_aliases: list[str] = [] powered_off_aliases: list[str] = [] for alias in required_aliases: vmx_pattern = rf"^VMX alias={re.escape(alias)}\s+.*\spresent=([01])$" power_pattern = rf"^VM_POWER alias={re.escape(alias)}\s+.*\srunning=([01])$" vmx_line = next((line for line in lines if re.search(vmx_pattern, line)), "") power_line = next((line for line in lines if re.search(power_pattern, line)), "") if not vmx_line: missing_fields.append(f"VMX alias={alias}") elif not _line_matches([vmx_line], rf"\spresent=1$"): missing_vmx_aliases.append(alias) if not power_line: missing_fields.append(f"VM_POWER alias={alias}") elif not _line_matches([power_line], rf"\srunning=1$"): powered_off_aliases.append(alias) status = "ready_console_verify_artifact" exit_code = 0 if secret_like_lines: status = "blocked_secret_like_console_output" exit_code = 76 elif missing_fields: status = "blocked_missing_required_console_verify_fields" exit_code = 75 elif _value_for(lines, "VMWARE_AUTOSTART_VERIFY_READY") != "1": status = "valid_console_verify_artifact_vmware_not_ready" summary = [ f"schema_version={SCHEMA_VERSION}", f"status={status}", f"required_aliases={_csv(list(required_aliases))}", f"missing_required_field_count={len(missing_fields)}", f"missing_required_fields={_csv(missing_fields)}", f"secret_like_line_count={len(secret_like_lines)}", f"missing_vmx_aliases={_csv(missing_vmx_aliases)}", f"powered_off_aliases={_csv(powered_off_aliases)}", f"vmware_autostart_verify_ready={_value_for(lines, 'VMWARE_AUTOSTART_VERIFY_READY') or 'unknown'}", "secret_value_read=false", "password_prompt_allowed=false", "remote_write_performed=false", "host_reboot_performed=false", "vm_power_change_performed=false", "windows_update_policy_apply_performed=false", ] return exit_code, summary def main() -> int: parser = argparse.ArgumentParser( description=( "Validate no-secret stdout from windows99-vmware-autostart.ps1 -Mode Verify " "captured through RDP / Hyper-V / console." ) ) parser.add_argument("--input", required=True, help="Raw console stdout file.") parser.add_argument( "--output", help="Optional normalized artifact for --windows99-vmware-file.", ) parser.add_argument( "--required-aliases", default=",".join(DEFAULT_REQUIRED_ALIASES), help="Comma or space separated required VM aliases.", ) args = parser.parse_args() required_aliases = tuple( item for item in re.split(r"[\s,]+", args.required_aliases.strip()) if item ) or DEFAULT_REQUIRED_ALIASES input_path = Path(args.input) raw_text = input_path.read_text(encoding="utf-8", errors="replace") lines = _normalize(raw_text) exit_code, summary = validate(lines, required_aliases) if exit_code == 0 and args.output: output_path = Path(args.output) output_path.write_text("\n".join(lines + [""]) , encoding="utf-8") for line in summary: print(line) if args.output: print(f"normalized_output={args.output if exit_code == 0 else ''}") return exit_code if __name__ == "__main__": raise SystemExit(main())