diff --git a/scripts/test-vm-disk.sh b/scripts/test-vm-disk.sh index b6464e7b94..b386aa8c4c 100755 --- a/scripts/test-vm-disk.sh +++ b/scripts/test-vm-disk.sh @@ -67,9 +67,17 @@ field_seen=false agent_value='' lock_value='' while IFS= read -r line; do - if [[ $line =~ ^[A-Za-z][A-Za-z0-9_-]*: ]]; then - field_seen=true + if [[ $line =~ ^[[:space:]]*$ || $line =~ ^[[:space:]]*# ]]; then + continue fi + # qm's fields are unindented lowercase keys. Do not let one valid field + # turn malformed/truncated lock or agent lines into an absent setting. + # Unknown well-formed fields remain valid; never echo an unparsed line. + if [[ ! $line =~ ^[a-z][a-z0-9_-]*: ]]; then + printf '%s\n' 'VM current configuration contains an unrecognised line; preflight incomplete.' >&2 + exit 1 + fi + field_seen=true case "$line" in agent:*) if [[ $agent_seen == true ]]; then diff --git a/scripts/tests/test_vm_disk_diagnostics.py b/scripts/tests/test_vm_disk_diagnostics.py index 3e25f290fb..adce1fb149 100644 --- a/scripts/tests/test_vm_disk_diagnostics.py +++ b/scripts/tests/test_vm_disk_diagnostics.py @@ -185,6 +185,44 @@ class VMDiskDiagnosticsTest(unittest.TestCase): self.assertNotIn("preflight completed", result.stdout) self.assert_passive(calls, operations=READS[:1] if "status_output" in settings else READS) + def test_malformed_lines_cannot_hide_agent_or_lock_configuration(self): + lines = ( + " lock: backup", "\tlock: backup", "lock=backup", "lock : backup", + "Lock: backup", "LOCK: backup", "lock", "agent 1", "Agent: 1", + "warning " + PRIVATE, + ) + for line in lines: + for suffix in ("", "\n"): + with self.subTest(line=line, suffix=suffix): + config = f"agent: 1\nname: {PRIVATE}\n{line}{suffix}" + result, calls = self.exercise(settings={"config_output": config}) + self.assertEqual(result.returncode, 1, result.stdout) + self.assertIn("unrecognised line; preflight incomplete", result.stderr) + self.assertNotIn("Guest agent configured:", result.stdout) + self.assertNotIn("VM lock:", result.stdout) + self.assertNotIn("preflight completed", result.stdout) + self.assert_passive(calls) + + def test_blank_comments_and_unknown_well_formed_fields_remain_passive(self): + for agent in (None, "0", "1"): + with self.subTest(agent=agent): + config = (f"\n# {PRIVATE}\n \t\nfuture_read_option: {PRIVATE}\n" + f"\t# lock: backup {PRIVATE}\nname: {PRIVATE}\n") + if agent is not None: + config += f"agent: {agent}\n" + result, calls = self.exercise(settings={"config_output": config}) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("Guest agent configured: enabled" if agent == "1" + else "Guest agent configured: disabled", result.stdout) + self.assertIn("VM lock: not reported", result.stdout) + self.assert_passive(calls) + + result, calls = self.exercise(settings={"config_output": f"\n# {PRIVATE}\n \t\n"}) + self.assertEqual(result.returncode, 1) + self.assertIn("preflight incomplete", result.stderr) + self.assertNotIn("VM lock:", result.stdout) + self.assert_passive(calls) + def test_help_and_invalid_arguments_never_contact_proxmox(self): for args in ([], ["0"], ["-1"], ["100;touch BAD"], ["100", "--probe"], ["9999999999"], ["--probe"], ["--help"]):