The tree adds the installer, service and test paths admitted by this source commit. Nothing else changes about what may cross. Source-Sha: 3e4a3aa4ce092e9e3f51bc78daf3ddc14d9638f8 Policy-Sha: 3e4a3aa4ce092e9e3f51bc78daf3ddc14d9638f8 Tree-Digest: 89d6fce3828a0e0de0c8eeb41ee6750f4462217e2a04b5190297134d7f4a50c3
47 lines
2.6 KiB
Python
47 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Check Server package ownership and wizard tables after custody verification."""
|
|
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def export(msi, table):
|
|
return subprocess.check_output(["msiinfo", "export", msi, table], text=True)
|
|
|
|
|
|
def rows(msi, table):
|
|
lines = export(msi, table).splitlines()
|
|
return [dict(zip(lines[0].split("\t"), line.split("\t"))) for line in lines[3:] if line]
|
|
|
|
|
|
def verify(msi):
|
|
properties = {row["Property"]: row["Value"] for row in rows(msi, "Property")}
|
|
assert properties["ProductName"] == "RedFlag Server", "wrong product"
|
|
assert properties["UpgradeCode"].lower().strip("{}") == "8f3c9e6a-4b1d-4a7f-9c2e-1d6a8b2f5e91", "Server upgrade identity changed"
|
|
files = {row["FileName"].split("|")[-1] for row in rows(msi, "File")}
|
|
assert files == {"redflag-server.exe", "redflag.env.example"}, f"unexpected Server payload: {files}"
|
|
assert {row["Feature"] for row in rows(msi, "Feature")} == {"ServerFeature"}, "unexpected product mode"
|
|
components = {row["Component"]: row for row in rows(msi, "Component")}
|
|
assert int(components["ServerBinary"]["Attributes"]) & 256, "Server component is not 64-bit"
|
|
services = rows(msi, "ServiceInstall")
|
|
assert len(services) == 1 and services[0]["Name"] == "RedFlagServer", "unexpected service ownership"
|
|
controls = rows(msi, "ServiceControl")
|
|
assert len(controls) == 1 and controls[0]["Name"] == "RedFlagServer", "unexpected service control"
|
|
events = int(controls[0]["Event"])
|
|
assert not events & (1 | 16), "MSI must not start an unconfigured service"
|
|
assert events & (2 | 32 | 128) == (2 | 32 | 128), "stop/remove lifecycle missing"
|
|
dialogs = {row["Dialog"] for row in rows(msi, "Dialog")}
|
|
assert {"ServerWelcome", "ServerLicense", "ServerMaintenance", "ServerProgress", "ServerComplete", "ServerFailed", "ServerCancelled"} <= dialogs, "wizard incomplete"
|
|
controls_text = export(msi, "Control")
|
|
assert "GNU AFFERO GENERAL PUBLIC LICENSE" in controls_text, "full license text missing"
|
|
assert "How to Apply These Terms to Your New Programs" in controls_text, "license text truncated"
|
|
summary = subprocess.check_output(["msiinfo", "suminfo", msi], text=True)
|
|
assert "x64;" in summary, "Server AMD64 payload needs an x64 MSI"
|
|
print("Server package boundary verified; Windows UI and installation still require native acceptance.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
verify(sys.argv[1])
|
|
except (AssertionError, KeyError, ValueError, subprocess.CalledProcessError) as error:
|
|
sys.exit(f"Server package boundary failed: {error}")
|