All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m1s
CD Pipeline / build-and-deploy (push) Successful in 4m28s
CD Pipeline / post-deploy-checks (push) Successful in 1m45s
164 lines
5.9 KiB
Python
Executable File
164 lines
5.9 KiB
Python
Executable File
#!/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 = ("110", "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())
|