forked from Manuel/meeting-assistant
Qualify guest version through one-way raw evidence before permit
This commit is contained in:
@@ -86,11 +86,13 @@ static class NativeDiagnostic
|
||||
{
|
||||
success = true, profile = Profile, mode = full ? "full" : "readiness",
|
||||
helperSha256 = Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "MacOsNativeDiagnostic.cs"))),
|
||||
readinessSourceSha256 = Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "macos-native-readiness.sh"))),
|
||||
udifChecksumBindingSha256 = Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "macos-native-udif-checksums.py"))),
|
||||
baselineReadinessNormalized = true, readinessDiagnosticBlocksExcluded = 7,
|
||||
nativeVersionGetterBlocksExcluded = 2, nativeVersionGetterSequenceRestored = true,
|
||||
nativeVersionMethod = NativeVersionMethod, nativeVersionSource = NativeVersionSource,
|
||||
nativeVersionMaximumBytes = 4096, nativeVersionReplyLimitSeconds = 180,
|
||||
nativeVersionMaximumBytes = 4096, nativeVersionResponseRequired = false,
|
||||
nativeVersionCandidateIsQualifiedReadiness = false, nativeVersionBindingRequiredBeforePermit = true,
|
||||
nativeVersionFixtures = "native-system-version-fixtures.json", nativeProductVersionCommandRemoved = true,
|
||||
diskReadinessAttemptLimit = 1, diskCommandLimitSeconds = 120, diskThreadObservationLimitSeconds = 60, stackSamplingUsed = false,
|
||||
resultNegativeCases = 6, containerNegativeCases = 11, recoveryPositiveCases = 4, recoveryNegativeCases = 12,
|
||||
@@ -1227,7 +1229,7 @@ static class NativeDiagnostic
|
||||
var ready = await Command("docker", ["exec", id, "head", "-c", "257", state + "/native-system-version.request"], output, "capture-native-version-request", cancellation, requireSuccess: false);
|
||||
if (ready.ExitCode != 0 || string.IsNullOrEmpty(ready.Output)) return;
|
||||
// The built-in producer publishes the final marker after closing raw bytes.
|
||||
// A still incomplete marker remains pending; its guest wait is bounded.
|
||||
// A still incomplete marker remains pending within the outer Recovery deadline.
|
||||
var requestFields = ready.Output.Split('\n');
|
||||
if (ready.Output.Length <= 256 && (!ready.Output.EndsWith('\n') || requestFields.Length < 5 || !requestFields[^2].StartsWith("ready:", StringComparison.Ordinal))) return;
|
||||
File.WriteAllText(Path.Combine(output, "native-system-version.request"), ready.Output, new UTF8Encoding(false));
|
||||
@@ -1249,16 +1251,14 @@ static class NativeDiagnostic
|
||||
error = exception.Message;
|
||||
}
|
||||
Save(evidencePath, new NativeVersionEvidence(token, NativeVersionSource, raw.Length, Hash(raw), NativeVersionMethod, version, error is null, started.Elapsed.TotalMilliseconds, DateTimeOffset.UtcNow, error));
|
||||
var response = token + (error is null ? ":" + version : ":error:invalid_native_system_version") + "\n";
|
||||
var reply = Path.Combine(output, "native-system-version.reply");
|
||||
File.WriteAllText(reply, response, new UTF8Encoding(false));
|
||||
await Command("docker", ["cp", reply, id + ":" + state + "/native-system-version.reply.tmp"], output, "stage-native-version-reply", cancellation);
|
||||
await Command("docker", ["exec", id, "mv", state + "/native-system-version.reply.tmp", state + "/native-system-version.reply"], output, "publish-native-version-reply", cancellation);
|
||||
Console.WriteLine(error is null ? "[native-version] method=" + NativeVersionMethod + " source=" + NativeVersionSource + " version=" + version : "[native-version] method=" + NativeVersionMethod + " source=" + NativeVersionSource + " failed: invalid native file/request; see raw evidence.");
|
||||
if (error is not null) throw new InvalidOperationException("Native guest-file XML evidence failed; no installation permit: " + error);
|
||||
}
|
||||
|
||||
static void ValidateNativeVersionBinding(string output, string token, string readiness)
|
||||
{
|
||||
if (new[] { "native-system-version.json", "native-system-version.plist", "native-system-version.request" }.Any(name => !File.Exists(Path.Combine(output, name))))
|
||||
throw new InvalidOperationException("Native guest-file evidence is incomplete; no installation permit.");
|
||||
using var evidence = JsonDocument.Parse(File.ReadAllText(Path.Combine(output, "native-system-version.json")));
|
||||
using var result = JsonDocument.Parse(readiness);
|
||||
var raw = File.ReadAllBytes(Path.Combine(output, "native-system-version.plist"));
|
||||
@@ -1279,6 +1279,29 @@ static class NativeDiagnostic
|
||||
var fixture = Path.Combine(output, "validation-native-system-version");
|
||||
Directory.CreateDirectory(fixture);
|
||||
const string token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
var canonical = Encoding.UTF8.GetBytes("""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
\t<key>BuildID</key>
|
||||
\t<string>5B24CC0E-5244-11EF-A61B-8668C9DC12C6</string>
|
||||
\t<key>ProductBuildVersion</key>
|
||||
\t<string>23G93</string>
|
||||
\t<key>ProductCopyright</key>
|
||||
\t<string>1983-2024 Apple Inc.</string>
|
||||
\t<key>ProductName</key>
|
||||
\t<string>macOS</string>
|
||||
\t<key>ProductUserVisibleVersion</key>
|
||||
\t<string>14.6.1</string>
|
||||
\t<key>ProductVersion</key>
|
||||
\t<string>14.6.1</string>
|
||||
\t<key>iOSSupportVersion</key>
|
||||
\t<string>17.6</string>
|
||||
</dict>
|
||||
</plist>
|
||||
""".Replace("\\t", "\t", StringComparison.Ordinal) + "\n");
|
||||
if (canonical.Length != 603 || Hash(canonical) != "0a652705e311f0346f7570007651bc13f4c98670950d376ddc21e3a567016b2b") throw new InvalidOperationException("Actual run4192 canonical SystemVersion fixture bytes differ.");
|
||||
var valid = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plist version=\"1.0\"><dict><key>ProductName</key><string>macOS</string><key>ProductVersion</key><string>14.6</string></dict></plist>\n");
|
||||
string Request(byte[] raw) => token + "\n" + NativeVersionSource + "\n" + raw.Length + "\nready:" + token + "\n";
|
||||
byte[] Changed(string from, string to) => Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(valid).Replace(from, to, StringComparison.Ordinal));
|
||||
@@ -1287,6 +1310,7 @@ static class NativeDiagnostic
|
||||
var entity = Encoding.UTF8.GetBytes("<!DOCTYPE plist [<!ENTITY version SYSTEM 'file:///must-never-be-read'>]><plist version='1.0'><dict><key>ProductVersion</key><string>&version;</string></dict></plist>");
|
||||
(string Name, byte[] Raw, string Request, string? Version)[] parserCases =
|
||||
{
|
||||
("actual-canonical603", canonical, Request(canonical), (string?)"14.6.1"),
|
||||
(Name: "valid14", Raw: valid, Request: Request(valid), Version: (string?)"14.6"),
|
||||
("valid14patch", Changed("14.6", "14.6.1"), Request(Changed("14.6", "14.6.1")), (string?)"14.6.1"),
|
||||
("duplicate", duplicate, Request(duplicate), (string?)null),
|
||||
@@ -1349,23 +1373,35 @@ static class NativeDiagnostic
|
||||
var to = readiness.IndexOf(end, from, StringComparison.Ordinal);
|
||||
var producer = readiness[from..to];
|
||||
var mapped = ReplaceOnce(ReplaceOnce(producer, "[ -f \"$source\" ]", "[ -f \"$SYSTEM_VERSION_FIXTURE\" ]"), "< \"$source\"", "< \"$SYSTEM_VERSION_FIXTURE\"");
|
||||
(string Name, byte[]? Raw, string Owner, string Existing, string Reply, bool Success, bool Request)[] shellCases =
|
||||
var gateStart = readiness.IndexOf("# END native SystemVersion plist getter\n", StringComparison.Ordinal) + "# END native SystemVersion plist getter\n".Length;
|
||||
var candidateGate = readiness[gateStart..readiness.IndexOf("flush_outputs || finish false diagnostic_log_budget_exceeded", gateStart, StringComparison.Ordinal)];
|
||||
var disagreement = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(canonical).Replace("<dict>", "<dict><!--<key>ProductVersion</key><string>14.6</string>-->", StringComparison.Ordinal));
|
||||
(string Name, byte[]? Raw, string Owner, string Existing, string? Candidate, bool Readonly, bool Request, bool Permit)[] shellCases =
|
||||
{
|
||||
(Name: "valid", Raw: (byte[]?)valid, Owner: token, Existing: "", Reply: "valid", Success: true, Request: true),
|
||||
("missing", (byte[]?)null, token, "", "none", false, false),
|
||||
("nul", valid.Concat(new byte[] { 0 }).ToArray(), token, "", "none", false, false),
|
||||
("control-binary", valid.Concat(new byte[] { 1 }).ToArray(), token, "", "none", false, false),
|
||||
("oversize", Enumerable.Repeat((byte)'x', 4097).ToArray(), token, "", "none", false, false),
|
||||
("foreign-owner", valid, new string('b', 32), "", "none", false, false),
|
||||
("existing-request", valid, token, "request", "none", false, false),
|
||||
("existing-reply", valid, token, "reply", "none", false, false),
|
||||
("stale-reply", valid, token, "", "stale", false, true),
|
||||
("reply-bad-eof", valid, token, "", "bad-eof", false, true),
|
||||
("reply-overbound", valid, token, "", "overbound", false, true),
|
||||
("reply-nul", valid, token, "", "nul", false, true),
|
||||
("host-xml-error", "bplist00"u8.ToArray(), token, "", "valid", false, true),
|
||||
("reply-timeout", valid, token, "", "none", false, true)
|
||||
("actual-canonical603", canonical, token, "", "14.6.1", true, true, true),
|
||||
("valid-minified14", valid, token, "", "14.6", true, true, true),
|
||||
("missing", null, token, "", null, false, false, false),
|
||||
("nul", valid.Concat(new byte[] { 0 }).ToArray(), token, "", null, false, false, false),
|
||||
("control-binary", valid.Concat(new byte[] { 1 }).ToArray(), token, "", null, false, false, false),
|
||||
("oversize", Enumerable.Repeat((byte)'x', 4097).ToArray(), token, "", null, false, false, false),
|
||||
("stale-owner", valid, new string('b', 32), "", null, false, false, false),
|
||||
("stale-request", valid, token, "request", null, false, false, false),
|
||||
("stale-raw", valid, token, "plist", null, false, false, false),
|
||||
("numeric-duplicate", duplicate, token, "", "14.6", true, true, false),
|
||||
("numeric-nested", nested, token, "", "14.6", true, true, false),
|
||||
("numeric-bogus", Changed("<string>macOS</string>", "<bogus/>"), token, "", "14.6", true, true, false),
|
||||
("version-disagreement", disagreement, token, "", "14.6", true, true, false),
|
||||
("below14", Changed("14.6", "13.6"), token, "", "13.6", false, true, false),
|
||||
("candidate-invalid", Changed("14.6", "14.6junk"), token, "", null, false, true, false),
|
||||
("binary-plist", "bplist00"u8.ToArray(), token, "", null, false, true, false)
|
||||
};
|
||||
bool Eligible(string state, string candidate)
|
||||
{
|
||||
var result = JsonSerializer.Serialize(new { token, success = true, osVersion = candidate, architecture = "x86_64", uid = 0, disk = "/dev/disk1", diskBytes = GuestDiskBytes, readOnly = false, systemExit = 0, diskArbitrationExit = 0, recoveryExit = 0, diskListExit = 0 });
|
||||
try { ValidateResult(result, token); ValidateNativeVersionBinding(state, token, result); return true; }
|
||||
catch (Exception exception) when (exception is InvalidOperationException or XmlException or DecoderFallbackException) { return false; }
|
||||
}
|
||||
if (Eligible(Path.Combine(fixture, "absent-evidence"), "14.6")) throw new InvalidOperationException("Early numeric readiness result bypassed missing evidence.");
|
||||
var shellReceipts = new List<object>();
|
||||
foreach (var test in shellCases)
|
||||
{
|
||||
@@ -1375,42 +1411,32 @@ static class NativeDiagnostic
|
||||
if (test.Raw is not null) File.WriteAllBytes(raw, test.Raw);
|
||||
File.WriteAllText(Path.Combine(state, "run.owner"), test.Owner + "\n");
|
||||
if (test.Existing != "") File.WriteAllText(Path.Combine(state, "native-system-version." + test.Existing), "stale\n");
|
||||
await Command("/usr/bin/mkfifo", ["-m", "600", Path.Combine(state, "wait.fifo")], output, "native-version-fifo-" + test.Name, CancellationToken.None);
|
||||
var body = test.Name == "reply-timeout" ? ReplaceOnce(mapped, "SECONDS - started < 180", "SECONDS - started < 1") : mapped;
|
||||
var script = "set -u\nSTATE_DIR=\"$1\"; SYSTEM_VERSION_FIXTURE=\"$2\"; PROOF_TOKEN=\"$3\"; SCALAR=\"\"\nexec 3> \"$STATE_DIR/proof.log\"\nexec 9<> \"$STATE_DIR/wait.fifo\"\n" + body + "\nif read_native_system_version; then printf 'disk-boundary\\n' > \"$STATE_DIR/disk-boundary\"; exit 0; else printf 'failed:%s\\n' \"$NATIVE_VERSION_ERROR\"; exit 1; fi\n";
|
||||
var script = "set -u\nSTATE_DIR=\"$1\"; SYSTEM_VERSION_FIXTURE=\"$2\"; PROOF_TOKEN=\"$3\"; SCALAR=\"\"\nexec 3> \"$STATE_DIR/proof.log\"\nfail_probe() { printf 'failed:%s\\n' \"$1\"; exit 1; }\n" + mapped + "\nif read_native_system_version; then printf '%s\\n' \"$SCALAR\" > \"$STATE_DIR/candidate\"\n" + candidateGate + "printf 'readonly-boundary\\n' > \"$STATE_DIR/readonly-boundary\"; exit 0; else printf 'failed:%s\\n' \"$NATIVE_VERSION_ERROR\"; exit 1; fi\n";
|
||||
File.WriteAllText(Path.Combine(state, "producer.sh"), script);
|
||||
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(20));
|
||||
var child = Command("/bin/bash", ["-c", script, "native-version-producer-fixture", state, raw, token], output, "native-version-producer-" + test.Name, deadline.Token, requireSuccess: false);
|
||||
var result = await Command("/bin/bash", ["-c", script, "native-version-producer-fixture", state, raw, token], output, "native-version-producer-" + test.Name, deadline.Token, requireSuccess: false);
|
||||
var requestFile = Path.Combine(state, "native-system-version.request");
|
||||
string? request = null;
|
||||
while (!child.IsCompleted)
|
||||
{
|
||||
if (File.Exists(requestFile))
|
||||
{
|
||||
var current = File.ReadAllText(requestFile);
|
||||
if (current.EndsWith("\nready:" + token + "\n", StringComparison.Ordinal)) { request = current; break; }
|
||||
}
|
||||
await Task.Delay(10, deadline.Token);
|
||||
}
|
||||
if (request is not null && test.Reply != "none")
|
||||
{
|
||||
string response;
|
||||
try { response = token + ":" + ParseNativeSystemVersion(File.ReadAllBytes(Path.Combine(state, "native-system-version.plist")), request, token) + "\n"; }
|
||||
catch (Exception exception) when (exception is InvalidOperationException or XmlException or DecoderFallbackException) { response = token + ":error:invalid_native_system_version\n"; }
|
||||
response = test.Reply switch { "stale" => response.Replace(token, new string('b', 32), StringComparison.Ordinal), "bad-eof" => response.TrimEnd('\n'), "overbound" => response + new string('x', 81), "nul" => response + "\0", _ => response };
|
||||
var reply = Path.Combine(state, "native-system-version.reply");
|
||||
File.WriteAllText(reply + ".tmp", response);
|
||||
File.Move(reply + ".tmp", reply);
|
||||
}
|
||||
var result = await child;
|
||||
if (File.Exists(requestFile) && File.ReadAllText(requestFile).EndsWith("\nready:" + token + "\n", StringComparison.Ordinal)) request = File.ReadAllText(requestFile);
|
||||
var published = request is not null;
|
||||
var diskReached = File.Exists(Path.Combine(state, "disk-boundary"));
|
||||
if ((result.ExitCode == 0) != test.Success || diskReached != test.Success || published != test.Request)
|
||||
throw new InvalidOperationException("Native SystemVersion exact Bash producer/reply fixture failed: " + test.Name);
|
||||
var readonlyReached = File.Exists(Path.Combine(state, "readonly-boundary"));
|
||||
var candidateFile = Path.Combine(state, "candidate");
|
||||
var candidate = File.Exists(candidateFile) ? File.ReadAllText(candidateFile).TrimEnd('\n') : null;
|
||||
if ((result.ExitCode == 0) != test.Readonly || readonlyReached != test.Readonly || candidate != test.Candidate || published != test.Request)
|
||||
throw new InvalidOperationException("Native SystemVersion exact Bash candidate fixture failed: " + test.Name);
|
||||
if (published && !File.ReadAllBytes(Path.Combine(state, "native-system-version.plist")).SequenceEqual(test.Raw!)) throw new InvalidOperationException("Native version producer did not preserve exact bytes.");
|
||||
shellReceipts.Add(new { test.Name, result.ExitCode, requestPublished = published, diskReached, fixtureWaitSeconds = test.Name == "reply-timeout" ? 1 : 180 });
|
||||
string? qualifiedVersion = null; string? qualificationError = null;
|
||||
if (published)
|
||||
{
|
||||
try { qualifiedVersion = ParseNativeSystemVersion(test.Raw!, request!, token); }
|
||||
catch (Exception exception) when (exception is InvalidOperationException or XmlException or DecoderFallbackException) { qualificationError = exception.Message; }
|
||||
Save(Path.Combine(state, "native-system-version.json"), new NativeVersionEvidence(token, NativeVersionSource, test.Raw!.Length, Hash(test.Raw!), NativeVersionMethod, qualifiedVersion, qualificationError is null, 0, DateTimeOffset.UtcNow, qualificationError));
|
||||
}
|
||||
var eligible = Eligible(state, candidate ?? "14.6");
|
||||
if (eligible != test.Permit) throw new InvalidOperationException("Bash candidate bypassed mandatory host qualification: " + test.Name);
|
||||
shellReceipts.Add(new { test.Name, result.ExitCode, requestPublished = published, readonlyReached, candidate, qualifiedVersion, permitEligible = eligible });
|
||||
}
|
||||
Save(Path.Combine(output, "native-system-version-fixtures.json"), new { success = true, parserCases = parserReceipts, bindingPositiveCases = 1, bindingNegativeCases = bindingCases.Length + 1, producerCases = shellReceipts, producerSha256 = Hash(Encoding.UTF8.GetBytes(producer)), sourcePathMappedOnlyForFixtureRead = true, actualBash = "/bin/bash", productionWaitSeconds = 180, timeoutFixtureWaitSeconds = 1, requestCommitLineRequired = true, dockerExecuted = false, guestExecuted = false });
|
||||
Save(Path.Combine(output, "native-system-version-fixtures.json"), new { success = true, parserCases = parserReceipts, bindingPositiveCases = 1, bindingNegativeCases = bindingCases.Length + 1, earlyResultWithoutEvidenceRejected = true, producerCases = shellReceipts, sourceBoundHashes = new { controller = Hash(File.ReadAllBytes("tools/ci/MacOsNativeDiagnostic.cs")), readiness = Hash(Encoding.UTF8.GetBytes(readiness)), producer = Hash(Encoding.UTF8.GetBytes(producer)), actualCanonical603 = Hash(canonical) }, sourcePathMappedOnlyForFixtureRead = true, actualBash = "/bin/bash", candidateIsQualifiedReadiness = false, hostBindingRequiredBeforePermit = true, responseRequired = false, requestCommitLineRequired = true, dockerExecuted = false, guestExecuted = false });
|
||||
}
|
||||
|
||||
static void ReportCpuPreflight(string output, string logs)
|
||||
|
||||
Reference in New Issue
Block a user