Uh oh!
There was an error while loading. Please reload this page.
Fix scoped ETW telemetry validation - #791
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR expands Windows ETW telemetry and separates it from local diagnostic audit logging.
Changes:
- Adds specialized lifecycle, policy, network, teardown, and rejection telemetry.
- Adds structured local audit records and policy hashing.
- Hardens diagnostic pipes and expands tests/documentation.
Reviewed changes
Copilot reviewed 32 out of 33 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
docs/diagnostics.md | Documents diagnostic sinks and pipe tokens. |
docs/telemetry/telemetry.md | Defines ETW and local audit contracts. |
src/Cargo.lock | Records SHA-256 dependency. |
src/Cargo.toml | Adds workspace SHA-256 dependency. |
src/backends/appcontainer/common/src/appcontainer_runner.rs | Adds AppContainer audit emissions. |
src/backends/appcontainer/common/src/base_container_runner.rs | Adds BaseContainer lifecycle telemetry. |
src/backends/appcontainer/common/src/dispatcher.rs | Records enforcement degradation. |
src/backends/appcontainer/common/src/fallback_detector.rs | Adds bounded degradation reasons. |
src/backends/appcontainer/common/src/job_object.rs | Exposes termination failures. |
src/backends/appcontainer/common/src/network_manager.rs | Tracks network setup and cleanup outcomes. |
src/backends/appcontainer/common/src/proxy_coordinator.rs | Reports proxy teardown activity. |
src/backends/isolation_session/common/src/manager.rs | Adds process and teardown telemetry. |
src/backends/isolation_session/common/src/one_shot.rs | Audits one-shot cleanup. |
src/backends/isolation_session/common/src/state_aware.rs | Audits lifecycle cleanup and execution. |
src/core/mxc_engine/src/dispatch.rs | Emits streaming policy hashes. |
src/core/mxc_engine/src/lib.rs | Exports policy-hash logging. |
src/core/mxc_engine/src/run.rs | Emits run policy and degradation records. |
src/core/mxc_engine/src/state_aware.rs | Handles the new parse-error variant. |
src/core/wxc/src/main.rs | Integrates rejection and state-aware auditing. |
src/core/wxc_common/Cargo.toml | Adds SHA-256 dependency. |
src/core/wxc_common/src/audit.rs | Defines structured local audit records. |
src/core/wxc_common/src/config_deserialize.rs | Shares secret-field detection. |
src/core/wxc_common/src/config_parser.rs | Classifies malformed one-shot JSON. |
src/core/wxc_common/src/diagnostic.rs | Redacts diagnostics and secures pipe naming. |
src/core/wxc_common/src/lib.rs | Exposes audit and policy identity modules. |
src/core/wxc_common/src/logger.rs | Adds structured diagnostic sink routing. |
src/core/wxc_common/src/models.rs | Adds canonical network strings. |
src/core/wxc_common/src/policy_identity.rs | Implements canonical policy hashing. |
src/core/wxc_common/src/telemetry/events.rs | Adds specialized telemetry wrappers. |
src/core/wxc_common/src/telemetry/mod.rs | Exports specialized telemetry APIs. |
src/mxc_telemetry/src/lib.rs | Implements specialized ETW events. |
src/tools/mxc_diagnostic_console/src/main.rs | Hardens the pipe and parses audit envelopes. |
tests/scripts/run_telemetry_etw_smoke_test.ps1 | Makes ETW validation provider-aware. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if !self.audit_enabled() { | ||
| return; | ||
| } | ||
| let (status, skip_reason) = appcontainer_teardown_status_with_bfs( | ||
| self.preserve_policy, | ||
| network.firewall_removal_ok, | ||
| bfs_requested, | ||
| bfs_removed, | ||
| ); |
| if logger.has_diagnostic_sink() { | ||
| let record = AuditEvent::new(AuditEventName::PolicyHash) | ||
| .str("backend", backend) | ||
| .str( | ||
| "policy_hash", | ||
| &wxc_common::policy_identity::state_aware_policy_hash( | ||
| &parsed.request, | ||
| backend, | ||
| phase, | ||
| phase_config, | ||
| ), | ||
| ) | ||
| .str("config_schema_version", &parsed.request.schema_version); | ||
| logger.log_audit_event(&record); |
| /// Render a message so it is safe to write to the diagnostic console TTY. | ||
| /// | ||
| /// Two orthogonal concerns are handled: | ||
| /// | ||
| /// 1. **Control-character hardening.** Anything below `0x20` other than `\t` | ||
| /// or `\n` is escaped, so a rogue client cannot inject terminal escape | ||
| /// sequences (cursor moves, colour changes, title updates) into the shared | ||
| /// console. | ||
| /// 2. **Lossless rendering of high Unicode.** Non-ASCII characters go through | ||
| /// [`char::escape_default`], which emits `\u{NNNN}` for anything outside | ||
| /// the printable ASCII range. The previous implementation masked the | ||
| /// Unicode scalar with `& 0xff` and rendered `\xNN`, which collided for | ||
| /// every pair of characters whose scalars agreed in the low byte (e.g. | ||
| /// `\u{0100}` and `\u{0200}` both rendered as `\x00`). |
| let mut outcome = mxc_engine::run_state_aware(parsed, dry_run); | ||
| Logger::clear_thread_diagnostic_sink(); |
| log_config_rejected( | ||
| &mut logger, | ||
| rejection_reason_for(&e), | ||
| &backend_name_for_state_aware(&parsed), | ||
| "", | ||
| parsed.phase.as_str(), | ||
| ); |
| let mut config = phase_config.cloned().unwrap_or(Value::Null); | ||
| strip_keys(&mut config, &["user", "wamToken", "upn", "token", "secret"]); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 33 changed files in this pull request and generated 1 comment.
Suppressed comments (6)
src/core/wxc/src/main.rs:428
- The mapping is not used for validation failures returned by
mxc_engine::run_state_aware: after dispatch, theErrarm only callslog_state_aware_dispatch_error. Backend validation errors such as IsolationSession's invalid UPN/token therefore produce neither the local nor ETWConfigRejectedrecord, contradicting the stated state-aware M-ETW-7 coverage. Emit the rejection beforeemit_state_awareshuts the provider down for config/phase/ID error codes.
fn rejection_reason_for(error: &MxcError) -> RejectionReason {
match error.code {
MxcErrorCode::MalformedRequest => RejectionReason::SchemaViolation,
MxcErrorCode::MalformedId => RejectionReason::IdentityShapeInvalid,
MxcErrorCode::PolicyValidation => RejectionReason::UnsupportedFieldForBackend,
MxcErrorCode::UnsupportedContainment => RejectionReason::UnsupportedContainment,
MxcErrorCode::UnsupportedPhase => RejectionReason::UnsupportedPhase,
MxcErrorCode::BackendUnavailable
| MxcErrorCode::StaleId
| MxcErrorCode::NotProvisioned
| MxcErrorCode::NotStarted
| MxcErrorCode::AlreadyStarted
| MxcErrorCode::AlreadyStopped
| MxcErrorCode::BackendError => RejectionReason::RunnerUnavailable,
}
src/backends/appcontainer/common/src/appcontainer_runner.rs:1639
- The successful AppContainer teardown path never emits
MXC.SandboxTornDownto ETW. This early return gates the whole remainder on a diagnostic sink, and unlike the pre-spawnteardownpath there is no telemetry call afterward. Consequently T2/T3 runs miss M-ETW-5 even when the provider is active; compute the status first, emit ETW independently, then gate only the JSON audit record.
if !self.audit_enabled() {
return;
}
src/core/wxc/src/main.rs:558
- State-aware
MXC.PolicyHashis still tied exclusively to the local diagnostic sink. When telemetry is enabled without--log-file/the pipe, this block is skipped and no ETW policy-hash event is emitted, despite the documented M-ETW-3 state-aware coverage. Compute the hash when either sink is active and calltelemetry::log_policy_hashwhentelemetry_active; only the JSON record should depend onhas_diagnostic_sink().
if logger.has_diagnostic_sink() {
let record = AuditEvent::new(AuditEventName::PolicyHash)
.str("backend", backend)
.str(
"policy_hash",
&wxc_common::policy_identity::state_aware_policy_hash(
&parsed.request,
backend,
phase,
phase_config,
),
)
.str("config_schema_version", &parsed.request.schema_version);
logger.log_audit_event(&record);
}
src/core/wxc/src/main.rs:370
- This ETW call is a no-op for every rejection that occurs during request loading (and for the state-aware command-override checks): the provider is initialized only later at lines 496-506 or 1261-1271. Thus malformed/schema-invalid requests never produce the documented
MXC.ConfigRejectedETW event even when their payload containsexperimental.telemetry.enabled=true. The telemetry opt-in must be resolved and the provider registered before these rejection sites, or the documented M-ETW-7 coverage must be narrowed.
wxc_common::telemetry::log_config_rejected(
correlation_id,
backend,
reason.as_str(),
offending_field,
);
src/tools/mxc_diagnostic_console/src/main.rs:448
- The documented terminal hardening has no implementation: no function follows this comment, and
DisplayEvent::Messagestill interpolates each untrusted pipe message directly intoprintln!at lines 652-669. A client that knows the session token can therefore inject ESC/control sequences into the console. Add the described escaping function and apply it before all terminal rendering (while preserving the desired raw/escaped behavior for collection files).
src/backends/appcontainer/common/src/proxy_coordinator.rs:481 proxy_stoppedreports only that the coordinator was active, not that cleanup succeeded.signal_process_cleanupcan failSetEventor time out, andremove_loopback_exemptiondiscards command failure, yet this returnstrue; callers then emit a successful teardown withproxy_stopped=true. Propagate those cleanup outcomes and include a proxy failure in the aggregate teardown status.
pub fn stop(&mut self, logger: &mut Logger) -> bool {
let was_active = self.is_active();
signal_process_cleanup(
self.shim_cleanup_event.take(),
self.shim_process_handle.take(),
"winhttp-proxy-shim",
logger,
);
signal_process_cleanup(
self.test_proxy_cleanup_event.take(),
self.test_proxy_handle.take(),
"wxc-test-proxy",
logger,
);
self.proxy_address = None;
if let Some(path) = self.shim_ready_file_path.take() {
let _ = std::fs::remove_file(&path);
}
if let Some(path) = self.test_proxy_ready_file_path.take() {
let _ = std::fs::remove_file(&path);
}
if let Some(container_name) = self.loopback_container_name.take() {
remove_loopback_exemption(&container_name);
}
was_active
| } | ||
| } | ||
| } | ||
| other => redact_secret_fields_at_path(other, &[]), |
4561c05 to
920a593CompareThere was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 33 changed files in this pull request and generated no new comments.
Suppressed comments (10)
src/tools/mxc_diagnostic_console/src/main.rs:448
- The documented control-character hardening is not implemented here: this comment is attached to
get_client_pid, whileclient_readerforwards parsed or raw client text anddisplay_loopinterpolates it directly intoprintln!. An ESC byte from a client can therefore still execute terminal control sequences. Add the described renderer and apply it before console output (while retaining an appropriate representation for collected logs).
src/core/wxc_common/src/audit.rs:547 - These shape checks do not prove that MXC minted the identity.
containerIdis copied directly from caller configuration, and the ProcessContainer call sites pass it here, so a caller can choosesandbox-0123456789abcdeforiso:ticket-123and have that value emitted verbatim despite the stated rule that caller-supplied IDs are always redacted. Preserve IDs only when the call site can provide trusted provenance; sanitize arbitrarycontainerIdvalues unconditionally.
let mxc_opaque = identity
.split_once(':')
.map(|(prefix, token)| {
matches!(prefix, "iso" | "wsb")
&& !token.is_empty()
&& token
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_' || b == b'.')
})
.unwrap_or(false);
if mxc_opaque {
identity
src/backends/appcontainer/common/src/proxy_coordinator.rs:481
- This returns whether the proxy was active before cleanup, not whether it was stopped successfully.
signal_process_cleanupcan time out or fail to signal, and loopback-exemption removal discards its result, yet callers now record this value asproxy_stopped=trueand may report a successful teardown. Return and aggregate the actual cleanup outcomes instead ofwas_active.
pub fn stop(&mut self, logger: &mut Logger) -> bool {
let was_active = self.is_active();
signal_process_cleanup(
self.shim_cleanup_event.take(),
self.shim_process_handle.take(),
"winhttp-proxy-shim",
logger,
);
signal_process_cleanup(
self.test_proxy_cleanup_event.take(),
self.test_proxy_handle.take(),
"wxc-test-proxy",
logger,
);
self.proxy_address = None;
if let Some(path) = self.shim_ready_file_path.take() {
let _ = std::fs::remove_file(&path);
}
if let Some(path) = self.test_proxy_ready_file_path.take() {
let _ = std::fs::remove_file(&path);
}
if let Some(container_name) = self.loopback_container_name.take() {
remove_loopback_exemption(&container_name);
}
was_active
src/backends/appcontainer/common/src/appcontainer_runner.rs:1816
ProcessExitedis emitted only from blockingwait(). The publictry_wait()can return the terminal exit code, and the FFI explicitly recommends polling it for cancellable waits; a caller that observesSome(code)and frees the handle never calls this branch, so the required process-outcome event is lost. Move once-only terminal-event emission into logic shared bytry_wait()andwait().
if !self.kill_requested {
wxc_common::telemetry::log_process_event(
wxc_common::telemetry::ProcessEventKind::Exited,
sanitize_identity(&self.identity),
self.pid,
wxc_common::telemetry::ProcessEventData::ExitCode(exit_code),
);
src/backends/appcontainer/common/src/base_container_runner.rs:2735
ProcessExitedis emitted only from blockingwait(). The publictry_wait()can return the terminal exit code, and the FFI explicitly recommends polling it for cancellable waits; a caller that observesSome(code)and frees the handle never calls this branch, so the required process-outcome event is lost. Move once-only terminal-event emission into logic shared bytry_wait()andwait().
if !self.kill_requested {
wxc_common::telemetry::log_process_event(
wxc_common::telemetry::ProcessEventKind::Exited,
sanitize_identity(&self.identity),
self.pid,
wxc_common::telemetry::ProcessEventData::ExitCode(exit_code),
);
src/mxc_telemetry/src/lib.rs:316
- The specialized kill-failure payload never carries the failure itself: callers pass
terminate_job_object/terminate_processhere, somxc.error_typedescribes the attempted method while the Win32/HRESULT error code is emitted only to the separate JSON audit sink. This does not satisfy the documented M-ETW-1errorfield. Add a bounded numeric error-code field and plumb the actual error through this API.
docs/telemetry/telemetry.md:98 - This row overstates ETW coverage for configuration rejection.
wxc-execinitializes telemetry only afterload_mxc_request_with_optionssucceeds, so malformed JSON and schema failures handled by the preceding error arms call an unregistered provider and reach only the local diagnostic sink. Clarify that M-ETW-7 covers post-parse rejections, or provide a consent-preserving initialization path for pre-parse failures.
| Configuration rejection (M-ETW-7) | `MXC.ConfigRejected` | `correlationId` or identity, bounded reason/error code, offending field path | ProcessContainer and IsolationSession validation paths |
tests/scripts/run_telemetry_etw_smoke_test.ps1:190
- These are two independent whole-document predicates, so they do not establish that any counted
<Event>belongs to this provider: the GUID may appear in tracer metadata while$eventCountincludes a different event. Parse the XML and count only event nodes whoseSystem/Provider/@Guidmatches$providerGuid; use that filtered count for the pass condition.
src/core/wxc_common/src/telemetry/events.rs:402 - The new process-event test sink discards the event-specific payload (
exit_code,timeout_ms, or kill error/type), so the added test can pass even if those values are wired to the wrong ETW event. CaptureProcessEventDatainCapturedRequirement(or a typed captured-process record) and assert each value, including the kill-failure payload.
fields: vec![
("identity".to_owned(), identity.to_owned()),
("process_id".to_owned(), process_id.to_string()),
],
docs/diagnostics.md:112
- The documented SID-less fallback cannot work on the server:
create_pipe_instancenow returns an error whencurrent_user_sid()isNone, so the console exits instead of creating\\.\pipe\mxc-diagnostics-{TOKEN}. Remove this fallback claim or implement a secure SID-resolution fallback consistently in both name construction and the pipe ACL.
authenticates the pipe. (If the SID cannot be resolved, the name degrades to
`\\.\pipe\mxc-diagnostics-{TOKEN}`.) Because the token is part of the name, the
920a593 to
0237400CompareThere was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 33 changed files in this pull request and generated no new comments.
Suppressed comments (9)
src/backends/appcontainer/common/src/proxy_coordinator.rs:481
proxy_stoppednow means only “a proxy was active,” not “it stopped successfully.” Both cleanup signals can fail or time out, and loopback-exemption removal discards its result, yet callers report thistruevalue in teardown telemetry. Return and aggregate the actual cleanup outcomes so failed cleanup is not reported as released.
was_active
src/core/wxc/src/main.rs:574
- This emits the caller-provided
sandboxIdbefore backend dispatch validates its encoded payload. A short forged value such asiso:alicepassessanitize_identityand reaches ETW verbatim even though the backend later rejects it, violating the documented no-caller-identifiers contract. Use a redacted marker until backend-specific ID validation succeeds, or move identity-bearing emission after validation.
let identity = state_aware_policy_identity(parsed.sandbox_id.as_deref());
wxc_common::telemetry::log_policy_hash(
&identity,
&policy_hash,
&parsed.request.schema_version,
);
src/core/wxc_common/src/telemetry/events.rs:402
- The test sink drops the event-specific payload, so these tests cannot detect swapped or incorrect
exit_code,timeout_ms, or kill-failure values—the payload semantics this PR is intended to validate. CaptureProcessEventDatain the test record and assert each concrete field/value.
.push(CapturedRequirement {
name: name.to_owned(),
fields: vec![
("identity".to_owned(), identity.to_owned()),
("process_id".to_owned(), process_id.to_string()),
],
tests/scripts/run_telemetry_etw_smoke_test.ps1:190
- These independent whole-document checks do not prove that any counted
<Event>belongs to Microsoft.MXC: the GUID may occur in tracer/session metadata while$eventCountcounts another event. Parse the XML and count only event nodes whoseSystem/Provider/@Guidmatches$providerGuid, then use that filtered count as the pass condition.
docs/diagnostics.md:112 - The documented SID-less fallback cannot work for the console:
create_pipe_instancereturns an error whencurrent_user_sid()is unavailable because it needs that SID for the DACL. Remove this fallback claim or implement a secure, consistent fallback in both naming and ACL creation.
authenticates the pipe. (If the SID cannot be resolved, the name degrades to
`\\.\pipe\mxc-diagnostics-{TOKEN}`.) Because the token is part of the name, the
docs/diagnostics.md:168
- Requiring High integrity here conflicts with the same document's claim that pipe messages work without elevation: a non-elevated console runs at Medium integrity and
Logger::verify_server_integrityrejects it. Either permit a same-user Medium-integrity server under the new token/ACL model or continue requiring elevation in the quick start and pipe documentation.
- Clients verify the pipe server runs at High integrity level or above before
sending data
src/core/wxc_common/src/audit.rs:45
- Correct the malformed doc text.
/// Closed set of audit record names. The `mxc.` prefix namespaces the record/// against unrelated lines sharing the same sink.
src/backends/appcontainer/common/src/appcontainer_runner.rs:1811
ProcessExitedis emitted only by blockingwait(). Public SDK/FFI consumers can observe completion viatry_wait()and then drop/free the handle, so that valid path produces no process-outcome event; repeatedwait()calls can also duplicate it. Route terminal observation through a shared once-only emitter used by bothtry_wait()andwait().
wxc_common::telemetry::log_process_event(
src/backends/appcontainer/common/src/base_container_runner.rs:2730
ProcessExitedis emitted only by blockingwait(). Public SDK/FFI consumers can observe completion viatry_wait()and then drop/free the handle, so that valid path produces no process-outcome event; repeatedwait()calls can also duplicate it. Route terminal observation through a shared once-only emitter used by bothtry_wait()andwait().
wxc_common::telemetry::log_process_event(
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated 1 comment.
Suppressed comments (9)
src/tools/mxc_diagnostic_console/src/main.rs:449
- The documented hardening is not implemented: these lines are rustdoc on
get_client_pid, andDisplayEvent::Messagetext is still interpolated directly intoprintln!later in this file. A diagnostic client can therefore inject ESC/control sequences into the operator's terminal. Add the described sanitizer and apply it to untrusted message text before terminal rendering.
docs/diagnostics.md:164 - A token in the pipe name is not primary access control because same-user processes can enumerate the named-pipe namespace and discover the endpoint without guessing. Until an in-band authentication mechanism is added, describe this only as session separation and identify the user-restricted DACL as the actual access control.
- `MXC_DIAG_PIPE_TOKEN` is the primary access control: it is mixed into the pipe
name, so a process that does not know the token cannot guess the endpoint.
`wxc-exec` refuses to connect at all when no valid token is set
docs/diagnostics.md:111
- The token separates concurrent sessions but cannot authenticate an enumerable named-pipe endpoint. This should not promise authentication until the client/server protocol verifies possession in-band.
the token both separates concurrent sessions for the *same* user and
authenticates the pipe. (If the SID cannot be resolved, the name degrades to
src/core/wxc/src/main.rs:395
- This assumes the parser path is bounded schema vocabulary, but unknown-field errors include the caller-controlled key itself (for example, existing parser tests produce
process.bogus). An attacker can therefore put arbitrary/high-cardinality text, including sensitive text, intomxc.offending_fieldand the local audit record. Emit only validated known paths (or omit unknown keys), and enforce a length bound before telemetry emission.
fn offending_field_from_message(message: &str) -> &str {
const PREFIX: &str = "Invalid configuration at `";
let Some(start) = message.find(PREFIX) else {
return "";
};
let field = &message[start + PREFIX.len()..];
field.split('`').next().unwrap_or("")
src/backends/appcontainer/common/src/appcontainer_runner.rs:1815
- Process outcome emission is tied only to
wait()and has no once guard. The public Rust SDK can observe completion throughtry_wait()and then drop the handle, producing noProcessExited; callingwait()repeatedly produces duplicate events. Record the first terminal observation in shared state and use the same helper from bothwait()andtry_wait().
let exit_code = code as i32;
if !self.kill_requested {
wxc_common::telemetry::log_process_event(
sanitize_identity(&self.identity),
self.pid,
wxc_common::telemetry::ProcessEvent::Exited(exit_code),
);
}
src/backends/appcontainer/common/src/base_container_runner.rs:2734
- Process outcome emission is tied only to
wait()and has no once guard. The public Rust SDK can observe completion throughtry_wait()and then drop the handle, producing noProcessExited; callingwait()repeatedly produces duplicate events. Record the first terminal observation in shared state and use the same helper from bothwait()andtry_wait().
let exit_code = code as i32;
if !self.kill_requested {
wxc_common::telemetry::log_process_event(
sanitize_identity(&self.identity),
self.pid,
wxc_common::telemetry::ProcessEvent::Exited(exit_code),
);
}
README.md:255
- Official grouped builds route every event using
MXC_EVENT_KEYWORD, including the newly added process, policy, network, teardown, and rejection events—not onlyMXC.ExecutionandMXC.Error. This understates what is Microsoft-routed when users opt in; update the disclosure to cover all provider events and their bounded payloads.
Official/shipped Microsoft builds set a TraceLogging provider group GUID at build time and route `MXC.Execution` and `MXC.Error` events to Microsoft through the UTC pipeline when telemetry is enabled — that same build-time setting also selects the correct Measures keyword and Product-and-Service-Usage privacy tag for the events, so telemetry routing and event classification always agree. **Local and open-source builds send nothing to Microsoft by default** — the public source ships without a provider group GUID, so events are emitted to the local ETW subsystem only, use a provider-local keyword with no UTC meaning, and carry no privacy classification tag, and are not routed to any Microsoft collection pipeline. Internal builds that set the `MXC_TELEMETRY_PROVIDER_GROUP_GUID` environment variable at build time enable the Microsoft-routed path.
src/core/wxc_common/src/logger.rs:336
write_allis not guaranteed to issue one OS write; it retries after partial writes. Because cloned logger handles and separate MXC processes append without a shared lock, another writer can interleave between those retries and corrupt the claimed one-record-per-line stream. Use a cross-process lock or a bounded singleWriteFileoperation whose partial-write failure is handled without retrying into the same record.
let _ = file.write_all(out.as_bytes());
docs/diagnostics.md:34
- The pipe name token selects a session but does not authenticate it because same-user processes can enumerate named pipes and learn the token. Calling it authentication overstates the security guarantee.
This issue also appears in the following locations of the same file:
- line 110
- line 162
| Env var | `MXC_DIAG_PIPE_TOKEN=<token>` | **Required** for pipe output. Selects the per-session pipe and authenticates it; use the same token for the console and `wxc-exec` |
| let suffix = diagnostic_pipe_token() | ||
| .map(|token| format!("-{token}")) | ||
| .unwrap_or_default(); |
Test coverage High finding fix: The requirement event test \ equirement_events_use_bounded_event_names\ now validates that: 1. All ProcessExited events include exit_code field with correct value 2. All ProcessTimedOut events include timeout_ms field with correct value 3. All ProcessKillFailed events include kill_method field with correct value 4. Event payloads match the data variant (reject mismatched identity+data pairs) 5. EnforcementDegraded events include all tier/reason/enforcement-level fields 6. PolicyHash events include policy_hash and config_schema_version fields 7. SandboxNetworkPolicyApplied events include enforcement mode and proxy port 8. SandboxTornDown events include status and released_resources 9. ConfigRejected events include all rejection metadata Enhanced the test sink's \ ecord_process\ function to capture the full payload (ProcessEventData::ExitCode/TimeoutMs/KillFailure) so tests can assert on actual field values, not just event names and identity. All 632 wxc_common + 17 mxc_telemetry tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84161020-6009-4a46-a320-79d062f2ffa8
…active Performance Medium finding fix: In emit_enforcement_degraded(), reorganize the early-exit checks so that when neither telemetry nor diagnostic sinks are active, we return immediately and never allocate the effective_level string or reason_codes string. The previous code always computed both strings after checking sinks are active, even though one sink path (telemetry-only) would compute both but ignore the reason_codes allocation in the diagnostic sink branch. After fix: - If both sinks inactive: return immediately - If both sinks active: compute both (shared path) - If only telemetry active: compute both, use reason_codes, skip diagnostic - If only diagnostic active: compute both, skip telemetry, use reason_codes Also changed check from \logger.has_diagnostic_sink()\ to \diagnostic_active\ variable (already computed above). All 632 wxc_common + 17 mxc_telemetry + dispatcher tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84161020-6009-4a46-a320-79d062f2ffa8
cf9203a to
0e04c43CompareHigh-severity fixes: - Refactor ProcessEventKind + ProcessEventData into single ProcessEvent enum to make mismatched kind/data pairs unrepresentable at compile time (7717c04) - Fix field names in test_sink from snake_case to PascalCase (ExitCode, TimeoutMs, mxc.error_type) to match actual ETW provider schema - Update all call sites to use new ProcessEvent API (3 runners × 3 variants) - Remove deprecated ProcessEventKind/Data from public exports (mod.rs) Medium-severity fixes: - Expand logging_sinks_active test from 2 to 4 boolean combinations - Fix misleading comment in dispatcher.rs on sink-inactive early exit path - Update test assertions to validate correct field values All 632 wxc_common tests pass (excluding pre-existing fallback test failure). Formatting and warnings validated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ff351c1b-421d-433d-b038-b61db4f1da12
0e04c43 to
30947f3CompareThere was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/tools/mxc_diagnostic_console/src/main.rs:439
- The new terminal-hardening documentation is not backed by any implementation: this doc block is attached to
get_client_pid, andDisplayEvent::Messagestill interpolates each client-controlledlinedirectly intoprintln!at lines 654–668. An ESC byte can therefore still inject terminal control sequences. Please add the described renderer and apply it before every TTY write (while retaining the original text for plain log files).
src/backends/appcontainer/common/src/appcontainer_runner.rs:1823 ProcessExitedis emitted only fromwait(), but the public Rust SDK and C FFI exposetry_wait()as a terminal polling path. A streaming caller that stops aftertry_wait()returnsSome(code)never reaches this block, so the promised process-outcome event is missing. Route bothtry_wait()andwait()through an idempotent terminal-event helper so polling emits exactly one event.
if !self.kill_requested {
wxc_common::telemetry::log_process_event(
sanitize_identity(&self.identity),
self.pid,
wxc_common::telemetry::ProcessEvent::Exited(exit_code),
);
src/backends/appcontainer/common/src/base_container_runner.rs:2764
ProcessExitedis emitted only fromwait(), while SDK/FFI streaming callers may finish by pollingtry_wait()until it returnsSome(code). That path currently returns the exit code without emitting this event, leaving successful streaming runs without the required outcome record. Please use a shared idempotent helper from both terminal paths.
if !self.kill_requested {
wxc_common::telemetry::log_process_event(
sanitize_identity(&self.identity),
self.pid,
wxc_common::telemetry::ProcessEvent::Exited(exit_code),
);
README.md:255
- This “What official builds send” paragraph still names only
MXC.ExecutionandMXC.Error, but this PR adds several events to the same provider (Process*,PolicyHash, network policy, teardown, and config rejection), and official builds route those too. Please update the disclosure or link the complete inventory indocs/telemetry/telemetry.md; otherwise the README understates the data sent by official builds.
Official/shipped Microsoft builds set a TraceLogging provider group GUID at build time and route `MXC.Execution` and `MXC.Error` events to Microsoft through the UTC pipeline when telemetry is enabled — that same build-time setting also selects the correct Measures keyword and Product-and-Service-Usage privacy tag for the events, so telemetry routing and event classification always agree. **Local and open-source builds send nothing to Microsoft by default** — the public source ships without a provider group GUID, so events are emitted to the local ETW subsystem only, use a provider-local keyword with no UTC meaning, and carry no privacy classification tag, and are not routed to any Microsoft collection pipeline. Internal builds that set the `MXC_TELEMETRY_PROVIDER_GROUP_GUID` environment variable at build time enable the Microsoft-routed path.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated no new comments.
Suppressed comments (6)
src/tools/mxc_diagnostic_console/src/main.rs:439
- This rustdoc is attached to
get_client_pid, but it describes a terminal-message renderer that does not exist in this file. As written, generated documentation falsely claims control-character and Unicode escaping; remove the stale block (or implement and document the renderer separately).
src/mxc_telemetry/src/lib.rs:316 - Use
Warninghere. The adjacentMXC.Errorcontract reserves Error/Critical for product faults that feed reliability alerting, whileProcessKillFailedis best-effort and may be caused by a normal process-exit race. Classifying that expected race asErrorproduces false reliability signals.
src/mxc_telemetry/src/lib.rs:327 - This field is not receiving an error type: every new caller passes a kill method such as
terminate_job_objectorterminate_process. Consumers therefore get an undocumented value inmxc.error_typeand no error reason/code, despite the M-ETW-1 contract requiring an error payload. Carry a bounded kill method plus numeric error code, or pass an actual bounded error category throughProcessEventand its callers.
src/core/wxc_common/src/policy_identity.rs:105 phase_configis the raw, permissive experimental JSON, so hashing it wholesale and removing only secret-looking key names breaks this module's explicit allow-list/privacy contract. For example, IsolationSessionappIdmay be any caller string and is explicitly not interpreted or enforced, yet it affects this deterministic hash; unknown non-secret-named fields do too. Project only typed enforcement fields and excludeappId/unknown fields so arbitrary caller data cannot become a confirmation oracle.
let mut config = phase_config.cloned().unwrap_or(Value::Null);
strip_keys(&mut config, &["user", "upn"]);
src/core/wxc_common/src/policy_identity.rs:110
- Including the lifecycle phase makes one sandbox's
PolicyHashdiffer across provision/start/exec/stop even when its enforcement policy is unchanged. The phase is already carried by lifecycle telemetry; exclude it from the policy projection so this remains a stable identity for the effective policy as documented.
"phase": phase,
src/backends/appcontainer/common/src/base_container_runner.rs:2759
kill_requestedremains false after this normal-exit event because BaseContainer later calls the internalterminate_and_reap()rather thanself.kill(). Since publicwait()only borrows the handle, a second call emitsProcessExitedagain; after a timeout, a later call emits an additional exit event. Track whether a terminal event has already been emitted (or mark both success and timeout outcomes consumed) so each process produces one outcome.
if !self.kill_requested {
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 37 out of 38 changed files in this pull request and generated no new comments.
Suppressed comments (6)
src/core/wxc_common/src/telemetry/events.rs:391
- The real
MXC.ProcessTimedOutevent writesmxc.timeout_ms(src/mxc_telemetry/src/lib.rs:306), notTimeoutMs. Keeping a different test-sink schema masks precisely the ETW payload mismatch this PR intends to catch.
src/core/wxc_common/src/policy_identity.rs:170 containerIdcan be caller-supplied identity material (including a UPN), yet it is included verbatim in the deterministic, unkeyed policy hash that official builds may emit to telemetry. This reintroduces the dictionary/confirmation oracle thatsanitize_identityexplicitly avoids for user identifiers: an observer can hash candidate IDs with otherwise-known defaults and compare them. Exclude or irreversibly sanitize identity-bearing IDs before hashing, or use a keyed construction whose key is not emitted.
src/core/wxc_common/src/telemetry/events.rs:383- The production provider emits this field as
mxc.exit_code(src/mxc_telemetry/src/lib.rs:285). UsingExitCodein the capture sink lets schema tests pass against a field that no real ETW event contains, so the new payload validation does not validate the production contract.
This issue also appears on line 391 of the same file.
CODE_REVIEW_TRACELOGGING_COMPLIANCE.md:93
- This assertion is factually reversed:
mxc_telemetrydeclaresmxc.exit_codeandmxc.timeout_ms(src/mxc_telemetry/src/lib.rs:285,306). As written, this compliance record documents and endorses a schema that the provider does not emit; update the assessment after aligning the test sink with the provider.
**Rationale:**
1. **Schema Consistency** — Event field names must match the emitting provider's schema. The ETW provider (implemented in `mxc_telemetry`) declares these fields in PascalCase; the test sink was using snake_case, which masked a mismatch.
2. **Type Safety in Practice** — This fix ensures that when real ETW consumers parse the event payload, field names match what the schema specifies.
3. **Privacy and Data Tagging Readiness** — Consistent field naming is prerequisite for applying privacy tags (PDT_*) at schema-generation time.
4. **Cross-Consumer Compatibility** — Any Kusto query or downstream telemetry pipeline depending on `ExitCode` (not `exit_code`) now sees consistent data.
CODE_REVIEW_TRACELOGGING_COMPLIANCE.md:300
- There is no Linux/LTTng backend: the entire non-Windows provider is implemented as no-op stubs returning inactive (
src/mxc_telemetry/src/lib.rs:456-520). The claims that events are emitted identically across platforms and only the transport differs are therefore incorrect and should describe this as a Windows-only ETW implementation.
**Pattern Used:**
The `mxc_telemetry` crate is Rust-native and exposes the same API across Windows, Linux, and potentially macOS, using platform-specific backends:
- Windows: Native ETW via `tracelogging` crate bindings
- Linux: LTTng via `tracelogging` crate support
- macOS: Future support via abstraction layer
**Compliance Assessment:** ✅ **EXCELLENT**
**Rationale:**
1. **Cross-Platform Consistency** — Events are emitted identically regardless of host OS; only the transport differs (ETW vs LTTng vs syslog).
2. **Asimov Best Practice #10** — Cross-Platform Support: "Abstract tracelogging APIs so that events can be emitted the same way from Windows, Linux, or other environments."
docs/telemetry/telemetry.md:434
- The IsolationSession teardown implementation never emits
client_unregistered; its record containssession_stoppedandagent_user_deprovisionedonly. Listing this field as part of the contract makes consumers expect data that cannot be produced.
| `mxc.SandboxTornDown` | Per-run resources released, once per handle | ProcessContainer: `backend`, `identity`, `tier`, `pid`, `status`, `firewall_rules_removed`, `firewall_removal_ok`, `bfs_removed`, `proxy_stopped`, `preserve_policy`, `container_released`, `skip_reason`. IsolationSession: `backend`, `identity`, `phase`, `status`, `session_stopped`, `agent_user_deprovisioned`, `client_unregistered` |
Gudge (MGudgin)
left a comment
There was a problem hiding this comment.
Summary
I verified these findings against PR #791 at head 424a486 before filing. The review covers 4 High, 13 Medium, and 4 Low findings; 20 are anchored to added lines and one PR-description finding is recorded below.
The branch is behind its current main base: baseRefOid=aa05d77, while the PR merge-base is 3a5d0f1. This review uses GitHub's actual three-dot PR range. The local and GitHub diffs both contain 10,436 lines (their normalized byte counts differ by 76 bytes from headers/encoding).
Verified clean, with receipts
- All 16 unique cited paths have non-empty
git diff --numstatoutput in this PR. No finding relies on a byte-identical out-of-diff file. - Every inline comment below was checked against the captured diff and lands on an added (
+) line. - Runner test counts increased (
appcontainer_runner.rs: 29 to 34; IsolationSessionmanager.rs: 1 to 4), but neither runner test module calls the telemetry capture sink; the integration-coverage finding is therefore about the new wiring, not an absence of all tests. - Cross-platform parity was reviewed separately and no accidental shared-build/API asymmetry was found.
Attribution adjustments
- The config file reread predates this PR; the performance comment is limited to the newly added ungated redaction and full-request rendering.
- The teardown buffer logger predates this PR; the maintainability comment is framed as a gap in the new diagnostic-sink preservation fix.
initialize_policypredates this PR; the simplicity comment targets the newly addeddescribe_policycopy and its divergent decision.
Finding in the PR description
Medium (proportionality) - The stated scope understates changes to enforcement paths. The description says network/DNS enforcement and fallback/DACL behavior are unchanged, but this PR rewrites control flow in fallback_detector.rs and network_manager.rs to collect telemetry fields. Final behavior appears restored, but commit bf8f8eb was required to repair a BaseContainer denied-path DACL regression introduced during that refactor. Please update the PR description to name these enforcement-path changes so the appropriate reviewers are engaged.
Verified pre-existing - not attributed to this PR
No byte-identical out-of-diff finding is being charged to this PR. The three pre-existing structures noted above are included only where the new code adds an ungated cost, leaves a gap in the new fix, or duplicates an existing decision.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| serde_json = "1" | ||
| serde_path_to_error = "0.1" | ||
| # SHA-256 for the canonical policy hash (`wxc_common::policy_identity`). | ||
| sha2 = "0.10" |
There was a problem hiding this comment.
Low (supply-chain) - Core use of sha2 0.10 further entrenches duplicate versions.
This new direct workspace use expands sha2 0.10 into wxc_common while sha2 0.11 is already present transitively. No new crate or MSRV regression is introduced, but the binary now carries the older implementation more broadly.
Fix: Track unification on one supported sha2 version when dependencies retaining 0.10 can move.
There was a problem hiding this comment.
Thanks for flagging this. Confirmed via cargo tree -i sha2: sha2 0.10.9 is pulled in transitively by several existing backends (lxc_common, bwrap_common, etc.) independent of this PR, and sha2 0.11.0 is present as a separate transitive dependency of an unrelated crate. This PR's new direct wxc_common -> sha2 0.10 dependency does not introduce a new duplicate pair -- it adds one more consumer of a version that was already duplicated on main before this change.
Unifying on a single sha2 version workspace-wide is a legitimate follow-up, but it requires coordinating a version bump across multiple unrelated backend crates that do not otherwise touch this PR's ETW/logging scope, so I'm deferring it rather than bundling it into this change. Tracking it as a separate housekeeping item is the right call here; no code change is being made in this PR for this comment.
…ation Fixes 20 review findings from the second MGudgin review round, all within the PR's logging/ETW scope: High severity: - ProcessKillFailed ETW now carries the real OS error code (mxc.error_code) alongside the bounded kill-method string. - ConfigRejected ETW now threads the state-aware phase through to mxc.phase; the wrapper accepted it but never forwarded it. - Telemetry test module now shares TEST_LOCK with events::test_sink instead of using a separate lock that didn't serialize against it. - Added runner-level e2e tests (real process + real job object + real file-backed Logger) proving the wait/kill/teardown -> audit-log wiring end to end, including a regression test for proactive-cancellation telemetry suppression. Medium severity: - Diagnostic pipe CreateFile now sets SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION to prevent client impersonation above the intended level. - State-aware policy-hash identity now goes through redact_identity so a UPN-shaped iso:/caller-supplied identity is never hashed/logged raw. - Raw-config redaction in wxc-exec is now gated behind logger.has_diagnostic_sink() instead of always doing the work and discarding the result. - Redaction path tracking in diagnostic.rs no longer clones the whole path vector per JSON key. - Canonical JSON hashing in policy_identity.rs now writes into a single growing Vec<u8> buffer instead of allocating a String per key/scalar. - Thread-local diagnostic sink installation now returns an RAII guard (ThreadDiagnosticSinkGuard) that clears the sink on drop, including on a panic unwind, instead of relying on a paired install/clear call. - Removed the stray duplicate schemas/dev/mxc-config.schema.0.dev.json (the canonical mxc-config.schema.0.8.0-dev.json already exists on main). - Removed the zero-call-site deprecated ProcessEventKind/ProcessEventData compat types. - network_manager.rs's initialize_policy now reuses describe_policy's NetworkPolicyPlan instead of duplicating the same decision. - Removed CODE_REVIEW_TRACELOGGING_COMPLIANCE.md, a leaked review artifact. Low severity: - Pipe token validation now checks Shannon entropy instead of a distinct-byte-count check that a repeated short pattern could satisfy. - Case-insensitive secret-field key matching no longer allocates a lower-cased copy of every key. - Added a named READ_CONTROL constant replacing a raw hex literal in the diagnostic pipe access mask. - Replied (no code change) to the sha2 0.10/0.11 duplicate-version observation: this PR adds one more consumer of an already-duplicated version rather than introducing a new duplicate pair; unification is tracked as separate follow-up work. Validated: cargo fmt --all --check, cargo clippy (wxc_common, appcontainer_common, isolation_session_common, mxc_telemetry, wxc) with -D warnings, full workspace build, and targeted tests across all five touched crates (1076 tests passing, 0 failed). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8517b504-0321-4b97-9b8b-aaddbb06e6a3
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/tools/mxc_diagnostic_console/src/main.rs:557
- Re-serializing
recordthroughserde_json::Valueloses the builder's field order (the default map is key-sorted), so pipe-delivered audit lines no longer begin with{"event":...}as the documented classifier requires. Preserve the raw JSON slice instead of converting the record back fromValue.
src/backends/appcontainer/common/src/base_container_runner.rs:2759 - A successful external
kill()sets this flag, so the subsequentwait()suppresses the only terminalProcessExitedrecord. The AppContainer path in this PR explicitly fixes the same gap; BaseContainer cancellations (and failed kills followed by natural exit) must also emit the observed exit outcome. Remove this suppression and the now-unnecessarykill_requestedstate.
if !self.kill_requested {
docs/diagnostics.md:49
- The documented rule does not match validation: four distinct characters have at most 2 bits/character and are rejected by the 3-bit Shannon threshold. Document the actual entropy threshold so users can diagnose tokens that the console refuses.
- at least **4 distinct** non-`-` characters.
src/backends/appcontainer/common/src/proxy_coordinator.rs:481
- This returns whether the proxy was active before cleanup, not whether it was stopped.
signal_process_cleanupcan failSetEventor time out, andremove_loopback_exemptiondiscards its result, yet callers will emitproxy_stopped=true; propagate the cleanup outcomes so teardown telemetry does not report a failed release as successful.
was_active
Gudge (MGudgin)
left a comment
There was a problem hiding this comment.
Re-reviewed current head b60e655.
Confirmed the ETW payload, shared telemetry test lock, pipe SQOS, identity redaction, no-sink performance gates, allocation fixes, teardown diagnostics, RAII cleanup, artifact removal, policy-plan reuse, entropy validation, and named access-mask fixes. The targeted verification suite passed 38 tests with 0 failures across wxc_common, appcontainer_common, and mxc_telemetry.
The sha2 version-unification follow-up is tracked in #844.
The remaining BaseContainer cancellation-outcome parity, broader runner-to-ETW integration coverage, and PR-description scope wording are non-blocking observations for this approval.
Resolve ETW telemetry conflicts while preserving current main APIs and live consent enforcement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa378d85-8a60-4f11-8ed3-485123cba810
There was a problem hiding this comment.
🟡 Changes recommended
Token validation permits weak values, proxy cleanup telemetry can be inaccurate, and several public instructions no longer match runtime behavior.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
docs/diagnostics.md:49
- These documented acceptance rules do not match
has_sufficient_entropy: four distinct characters can have only 2 bits/character and are rejected, while the implementation requires at least 3 bits/character. The length rule should also clarify that separators do not count once the validator is hardened; otherwise users can generate tokens that the documentation says are valid but MXC silently treats as absent.
- at least **32 characters**,
- only ASCII hex digits (`0-9`, `a-f`, `A-F`) and `-`,
- at least **4 distinct** non-`-` characters.
- Files reviewed: 34/35 changed files
- Comments generated: 4
- Review effort level: Balanced
| } | ||
| fn is_valid_pipe_token(token: &str) -> bool { | ||
| token.len() >= 32 |
| if let Some(container_name) = self.loopback_container_name.take() { | ||
| remove_loopback_exemption(&container_name); | ||
| } | ||
| was_active |
| #### What official builds send | ||
| Official/shipped Microsoft builds set a TraceLogging provider group GUID at build time and route `MXC.Execution` and `MXC.Error` events to Microsoft through the UTC pipeline when telemetry is enabled. **Local and open-source builds send nothing to Microsoft by default** — the public source ships without a provider group GUID, so events are emitted to the local ETW subsystem only and are not routed to any Microsoft collection pipeline. Internal builds that set the `MXC_TELEMETRY_PROVIDER_GROUP_GUID` environment variable at build time enable the Microsoft-routed path. | ||
| Official/shipped Microsoft builds set a TraceLogging provider group GUID at build time and route `MXC.Execution` and `MXC.Error` events to Microsoft through the UTC pipeline when telemetry is enabled — that same build-time setting also selects the correct Measures keyword and Product-and-Service-Usage privacy tag for the events, so telemetry routing and event classification always agree. **Local and open-source builds send nothing to Microsoft by default** — the public source ships without a provider group GUID, so events are emitted to the local ETW subsystem only, use a provider-local keyword with no UTC meaning, and carry no privacy classification tag, and are not routed to any Microsoft collection pipeline. Internal builds that set the `MXC_TELEMETRY_PROVIDER_GROUP_GUID` environment variable at build time enable the Microsoft-routed path. |
| ```powershell | ||
| # Terminal 1: start the diagnostic console (run as admin for ETW) | ||
| # Terminal 1: generate one token and start the diagnostic console |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa378d85-8a60-4f11-8ed3-485123cba810
There was a problem hiding this comment.
🔵 Needs a closer look
Identity correlation, lifecycle event completeness, cleanup reporting, and documentation contain unresolved correctness issues.
Review details
Suppressed comments (7)
Previously missed (5) — in code that hasn't changed since the last review.
src/backends/appcontainer/common/src/appcontainer_runner.rs:2103
- Process-exit emission occurs only in
wait(). A streaming caller can observeSome(exit_code)through the publicSandbox::try_wait()API and then drop the handle, producing noProcessExitedevent; repeatedwait()calls can also emit duplicates. Record the terminal outcome exactly once from both observation paths.
src/backends/appcontainer/common/src/base_container_runner.rs:2906 - Process-exit emission occurs only in
wait(). A streaming caller can observeSome(exit_code)through the publicSandbox::try_wait()API and then drop the handle, producing noProcessExitedevent; repeatedwait()calls can also emit duplicates. Record the terminal outcome exactly once from both observation paths.
src/core/mxc_engine/src/run.rs:109 - The ETW identity is derived from the requested
container_id, but BaseContainer replaces that identity at launch (for example, an empty/default ID becomes a generatedsandbox-…identity inbase_container_runner.rs:1271-1319). The resultingMXC.PolicyHashcarriesCLIwhile process and teardown events carry the generated identity, so these events cannot be joined as documented. Emit the hash after runner identity resolution or pass the resolved identity into this emission.
docs/diagnostics.md:49 - This rule does not match the implementation:
is_valid_pipe_tokenrequires at least 3 bits/character of measured Shannon entropy, not four distinct characters (a balanced four-character token has only 2 bits and is rejected). Document the actual threshold so users can diagnose rejected tokens.
docs/diagnostics.md:94 - Calling these events “local ETW only” is false for official builds: the same PR documents that a configured provider group routes them through UTC. Clarify the build-dependent behavior so the diagnostics guide does not contradict the telemetry contract.
src/backends/appcontainer/common/src/proxy_coordinator.rs:457
was_activedoes not establish that the proxy stopped.signal_process_cleanuponly logsSetEventand five-second wait failures, so this method returnstrueeven when a proxy process remains alive; teardown records then incorrectly reportproxy_stopped=true. Propagate and aggregate the actual cleanup results instead.
let was_active = self.is_active();
docs/diagnostics.md:20
- This Quick Start no longer says to elevate the console, but the client accepts a pipe server only at High integrity or above (
logger.rs:191-301). Following these commands in a normal terminal therefore makeswxc-execreject the console, contradicting the later claim that pipe messages work without elevation. Either require an elevated console throughout the documentation or relax the client requirement.
# Terminal 1: generate one token and start the diagnostic console
$env:MXC_DIAG_PIPE_TOKEN = [guid]::NewGuid().ToString("N")
$env:MXC_DIAG_PIPE_TOKEN
mxc-diagnostic-console.exe
- Files reviewed: 34/35 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Uh oh!
There was an error while loading. Please reload this page.
Resolve the telemetry and diagnostic overlap with PR #791 while preserving top-level telemetry consent, internal state-aware correlation, requested sandbox attribution, and the scoped ETW audit events. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e20d046-910e-4ee7-b8e8-acb0fe007208
📖 Description
This PR completes the Windows-local Microsoft.MXC ETW logging remediation. It separates ETW emission from local diagnostic sinks, corrects ETW payload semantics, strengthens payload and sink-gating tests, makes the ETW smoke test provider-aware, and documents the contract.
The scope is limited to logging/ETW emission, tests, performance, and documentation. It does not change network/DNS enforcement, fallback/DACL behavior, cleanup/kill behavior, execution policy, or add collectors/uploaders/services/daemons.
🔗 References
No linked issue.
🔍 Validation
cargo fmt --all -- --checkcargo build --workspace✅ Checklist
Cargo.lock, thedependency-feed-checkcheck passes (see docs/pull-requests.md)📋 Issue Type
Microsoft Reviewers: Open in CodeFlow