32 KiB
Security Policy
OpenShell policy defines what a sandboxed agent can access. The policy is enforced inside each sandbox by kernel controls, process setup, and the local policy proxy. The gateway stores and delivers policy, but it does not make per-request egress decisions.
For the field-by-field YAML reference, use Policy Schema Reference.
Policy Areas
| Area | Enforcement |
|---|---|
| Filesystem | Landlock restricts read-only and read-write paths. |
| Process | The supervisor launches the agent as an unprivileged user with reduced capabilities. |
| Network | The proxy evaluates destination, port, calling binary, and optional L7 rules. |
| Provider access | Attached provider profiles contribute endpoint and binary rules; credentials remain bound to profile-authorized endpoints. |
| Runtime settings | Typed settings are delivered with policy and can be global or sandbox scoped. |
Filesystem and process policy are startup-time controls. Network policy is dynamic and can be hot-reloaded when the new policy validates successfully.
Authored policy boundary
openshell-policy-schema is the sole owner of the authored YAML and JSON
representation. It preserves authored distinctions such as an absent
filesystem_policy versus an explicitly empty object, rejects duplicate keys,
and applies parser budgets while noyalib constructs the document. It also owns
pure language semantics such as access presets, MCP revision vocabulary,
effective ports and rule names, protocol classification, and lexical policy
path normalization.
Consumers project that syntax into purpose-specific models. openshell-policy
owns protobuf conversion, composition, merge behavior, raw-protobuf checks, and
validation that depends on runtime components. Both the proposal-risk prover and
standalone containment checker project the shared schema into their own models
using the same fail-closed parser as the runtime. The parser
requires version: 1 and rejects managed annotations and every unknown field
before any consumer-specific projection runs. There is no permissive parsing
profile: unsupported policy fields always invalidate the document. Middleware config, query and persisted-query names, and recursive MCP
parameter names are open user-data maps rather than schema extensions.
Before applying Landlock, the supervisor enriches baseline filesystem paths that
the runtime needs. Missing baseline paths are skipped so one absent runtime path
does not weaken the whole ruleset. When GPU devices are present, GPU baseline
enrichment adds existing GPU device nodes as read-write paths and promotes
/proc to read-write because CUDA workloads write thread metadata under
/proc/<pid>/task/<tid>/comm.
Landlock rules are tailored to the inode type reported by the already-opened
path descriptor. Directories retain the requested directory and file rights;
regular files, device nodes, sockets, and other non-directories retain only
file-compatible rights. This avoids rejecting valid mixed-path policies without
weakening hard_requirement: genuine unsupported ABI capabilities and
preparation failures still fail sandbox startup.
Network Decisions
Ordinary network traffic follows this order:
- Force traffic through the sandbox proxy with namespace and seccomp controls.
- Identify the calling binary and compare its trusted identity.
- Reject hard-blocked destinations, including unsafe internal IP ranges unless explicitly allowed.
- Match the destination and binary against network policy blocks.
- Apply optional HTTP/L7 rules for endpoints that enable protocol inspection.
- Allow, deny, audit, or log according to the matched policy.
Explicit deny and hardening checks win over allow rules. If no rule matches, the request is denied.
Host Wildcards
Network endpoint host patterns accept a * wildcard inside the first DNS
label and as an entire middle DNS label. The OPA runtime matches with a .
label boundary, so a wildcard never spans dots. The validator enforces the same
boundary so that policy load fails fast instead of silently mismatching at the
proxy.
| Pattern | Accepted | Example match | Notes |
|---|---|---|---|
*.example.com |
Yes | api.example.com |
Single first label of any value. |
**.example.com |
Yes | a.b.example.com |
Recursive wildcard as the entire first label. |
*-aiplatform.googleapis.com |
Yes | us-central1-aiplatform.googleapis.com |
Intra-label wildcard inside the first DNS label. |
*.s3.*.amazonaws.com |
Yes | bucket.s3.us-east-1.amazonaws.com |
Middle-label * matches exactly one DNS label. |
* or ** |
No | — | Matches every host. |
*.com, **.com |
No | — | TLD wildcards (labels <= 2). |
foo.us-*.example.com |
No | — | Partial middle-label wildcards are not allowed. |
foo.**.example.com |
No | — | Recursive wildcard outside the first label is not allowed. |
foo**.example.com |
No | — | Recursive ** mixed inside a label; allowed only as the entire first label. |
Validation rejects the disallowed patterns at policy load time. OPA load errors omit the supplied hostname. Exact hosts and IP addresses do not use this wildcard-validation path.
TLS and L7 Inspection
For HTTP endpoints that need request-level controls, the proxy can terminate TLS with the sandbox's ephemeral CA and inspect method/path or protocol-specific metadata before forwarding. The proxy also supports credential injection on terminated HTTP streams when policy allows the endpoint.
Static provider credentials have an independent endpoint-binding boundary. Provider profile endpoints define that boundary by default. An endpointless profile can delegate binding authority to sandbox policy through an endpoint that names the concrete attached provider instance. The gateway rejects unattached, profileless, endpointful, and gateway-global uses of that policy binding. Policy endpoint changes rotate the provider-environment revision so the supervisor installs policy and credential binding snapshots atomically.
Raw streams and long-lived response bodies are connection scoped. Policy
generation changes close relays pinned to the previous generation instead of
allowing them to continue under stale authorization. HTTP upgrades switch to
raw relay by default. A protocol: rest endpoint can opt in to
websocket_credential_rewrite for client-to-server WebSocket text messages
after an allowed 101 upgrade; server-to-client traffic and all other upgraded
protocols remain raw passthrough.
A protocol: tcp hostname is a connection-routing constraint, not an
application-authority boundary. Transparent capture validates the approved DNS
name, pinned destination address, port, and calling binary before opening the
stream, but it does not inspect TLS SNI, HTTP Host, or another protocol-level
destination. Compatible shared infrastructure can therefore let a client
select another tenant, virtual host, or service behind the approved front door.
Credentialed Endpoints
OpenShell keeps provider credentials on paths it can inspect or rewrite by default. The gateway derives credential provenance from the attached providers and stamps it onto the effective policy at composition time. This provenance is internal, contains no credential identifiers or values, and is never trusted from user-authored policy.
Every evaluation clears provenance across the whole policy and re-derives it from two sources:
- the endpoints of attached provider profiles that carry credentials, and
- the valid
credential_bindingentries of the sandbox policy that name an attached provider whose profile is endpointless.
A binding reduces to a host and port scope only. Dropping the path is
deliberate: a path is not observable on an L4 or tls: skip endpoint, so a
path-scoped derivation would omit the marker on exactly the surfaces the
uninspected-credential gate exists to catch. A malformed binding — empty
provider, missing host, or a port outside 1..=65535 — fails the evaluation
instead of contributing a scope. Bindings naming an endpointful profile or an
unattached provider contribute nothing; the gateway rejects those uses
separately.
Both sources merge into one deduplicated scope set, and each endpoint is stamped once per evaluation from that set, so binding-derived scopes reach the same gates as profile-derived ones. The stamp is an assignment, not an accumulation, so an endpoint that stops matching a credentialed scope — or whose binding was removed — loses its marker in the same pass. This must remain a full recomputation: a delta-based derivation would let a series of individually valid edits reach a state no single edit would have admitted.
Credentialed L4-only and tls: skip endpoints fail policy validation unless the
public allow_uninspected_credentials escape hatch is explicitly enabled. The
flag defaults to false and is security-flagged in policy approval flows.
Incremental merges only ever add the flag to a matching endpoint; clearing it
requires removing the endpoint or replacing the policy.
Image discovery may persist a desired policy for repair, but does not authorize workload activation. The gateway applies the credential gate after full provider composition and provenance derivation. A rejected effective configuration keeps startup blocked with a bounded diagnostic; the supervisor waits for management repair instead of launching with connection-time denials or a fallback policy. Accepted runtime state includes the matching provider-environment revision, so policy and credential updates cannot activate independently.
The network supervisor independently enforces the same boundary. Credentialed WebSocket upgrades use the parsed relay, binary frames fail closed, and text placeholders require rewrite. REST bodies continue streaming when body rewrite is disabled. The relay holds complete placeholder candidates until a request-scoped metadata snapshot can classify them. Authoritatively unknown keys and valid credentials with a current binding pass unchanged, including references bound to the destination itself. Revoked or invalid identities and unavailable classification state fail closed. Classification never substitutes secret values and shares the request's credential revision; stale credential or policy generations terminate forwarding. Header rewriting scans only headers. Body bytes received in the initial proxy read follow the same body classifier or explicit rewriter as later reads. Candidates are limited to 4096 wire bytes, including percent encoding. Malformed or oversized candidates fail closed; HTTP trailers retain prefix-based rejection. Explicitly opted-in endpoints retain raw passthrough behavior.
Body denials return credential_placeholder_in_request_body in a local HTTP 403
response and discard any partially written upstream request. Safe preceding bytes
may already have reached upstream.
Denials emit both the relevant network activity and a detection finding. Events identify only the destination, policy, traffic surface, and controlled denial reason; they never include credential names, placeholders, body content, or secret values.
Credential provenance is gateway-derived and deliberately absent from the policy YAML schema, so it does not survive a policy that never transits the gateway. Gateway-delivered policy is the authoritative source for this control, and a policy without provenance applies neither the raw-tunnel refusal nor the WebSocket binary-frame refusal. The request-body backstop still applies, because it keys off the presence of a secret resolver rather than endpoint provenance.
Two supervisor-local paths load a policy without provenance. A supervisor booting from an explicitly provisioned policy file has a bounded window before that policy is resynchronized to the gateway, which then serves a stamped effective policy. An explicit supervisor Rego and data override is permanent, because gateway revisions are observed for settings and providers but never replace the local policy. Workload-image files and environment variables cannot configure the separately isolated supervisor. When a supervisor override is combined with injected provider credentials, the supervisor emits a high-severity detection finding at startup naming the inactive controls.
Policy Load Diagnostics
OpaEngine bounds the error messages returned when loading or reloading policy from files, strings, or protobuf. Each message contains at most eight error items and 512 UTF-8 bytes, including its heading, separators, and any additional violations omitted marker. The loader reports complete, fixed categories and discards authored names, values, paths, source snippets, and nested error chains.
Typed validation categories distinguish process identity, filesystem paths and limits, Landlock compatibility, endpoint hosts and ports, credential signing and rewriting, MCP configuration, and middleware configuration. Opaque L7 errors identify the protocol-configuration or policy-validation stage; endpoint conflicts report ambiguous selectors. YAML errors retain a fixed parser category and numeric line and column when available. File I/O, Rego loading, and internal policy-data errors use fixed messages.
A candidate rejected during validation does not replace the active engine or advance its generation. The supervisor separately applies policy_validation_failure_mode and may publish a quarantine generation as described below. The diagnostic bounds cover returned OPA load errors; accepted-policy warnings, runtime request diagnostics, and gateway-authored policy parser messages have separate reporting contracts.
Live Updates
The gateway stores sandbox-authored policy revisions separately from derived effective sandbox configuration. Effective configuration can include gateway-global policy overrides and provider-profile policy layers. The supervisor polls for config revisions and attempts to load new dynamic policy into the in-process OPA engine; CLI reads of the latest sandbox policy use the same effective configuration path.
The OPA loader checks the object and list shapes of raw policy data before injecting runtime fields, normalizing values, or expanding access presets. It rejects the first malformed container with a fixed structural error that excludes authored keys and values. This check preserves valid versionless OPA data and runtime-only fields. A rejected OPA engine reload leaves that engine's installed policy, generation, and decisions unchanged; the supervisor separately applies its configured runtime rejection mode.
After validating L7 rules, the OPA loader converts nonempty string query and MCP parameter matchers into explicit glob objects, including MCP tool aliases and deny rules. Matchers representable in both YAML and protobuf therefore expose the same representation to endpoint configuration consumers. Already lowered glob and any matchers retain their values across reloads; normalization preserves runtime endpoint provenance. Empty scalar query matchers remain an OPA-only form because Rego gives them different behavior from empty glob objects.
An explicitly supplied MCP rule params value must be a map in both allow and deny rules. Omit the field when using only the tool alias; params: null is rejected before alias lowering. A rejected raw policy reload preserves the active evaluator and its generation.
The supervisor validates complete effective policy generations before activation. Overlapping endpoint selectors may contribute request allow and deny rules only when their connection and request-processing metadata agree; conflicting TLS, destination, credential, parser, or enforcement metadata rejects the complete generation. Plain L4 endpoints do not contribute request-processing metadata, so they may overlap an L7 endpoint when their connection metadata agrees. When request paths overlap, a path endpoint with a higher specificity rank deterministically overrides broader request-processing metadata. Equally specific overlapping endpoints must agree.
Endpoint tls, enforcement, and access use protobuf enums and retain their
named YAML spellings. tls admits only an omitted value, meaning auto-detect
and terminate for inspection, or skip; every other value, including the
removed terminate and passthrough spellings, is rejected. protocol
remains a string so the supported protocol set can evolve, but every ingress
validates it before persistence or activation.
The supervisor also refuses unknown enum numbers and protocol values
defensively; an unrecognized enforcement value never falls back to audit.
Gateway mutation paths validate the complete effective candidate before persistence when the affected sandbox scope is known. Direct replacements, incremental merges and approvals, provider attachment, and profile fanout reject ambiguity atomically, without creating an invalid revision or partially activating an update. Supervisor validation remains the defense-in-depth boundary for startup, concurrent changes, and sources outside those mutations.
L7 allow and deny append operations carry an explicit rule target and the complete affected binary and port scope. The merge engine resolves one non-provider endpoint within that rule, optionally by exact endpoint path, and compares both scope sets before mutation. A partial declaration, ambiguous target, or changed scope rejects the batch before revision persistence. The declaration records operator intent; it does not grant policy-writing authority or change the stored binary and port sets.
The [openshell.gateway] policy_validation_failure_mode configuration controls
candidates rejected by supervisor runtime validation. Gateway preflight
rejections never become generations and leave the active policy unchanged. The
runtime mode defaults to fail_closed, which publishes a quarantine generation,
denies new egress, invalidates existing relays, and leaves the previous policy
inactive. Operators may explicitly select
retain_last_valid, which keeps the previous generation active. With no
previous valid generation, the effective mode remains fail_closed regardless
of the configured mode. The gateway distributes this startup configuration to
sandbox supervisors with each effective policy snapshot. OCSF configuration and finding events state the
candidate version, validation rationale, configured and effective modes, active
generation, and whether the previous policy is active. Static controls,
such as filesystem allowlists and process identity, require a new sandbox
because they are applied before the child process starts.
Gateway-global policy can override sandbox-scoped policy. Use it sparingly because it changes the effective access model for every sandbox on the gateway.
Policy Advisor
The policy advisor pipeline turns observed denials into draft policy
recommendations. There are two proposers (sandbox-side mechanistic mapper,
agent-authored via policy.local); the gateway is the single referee.
When enabled, L7 policy_denied responses include both structured
next_steps and a short agent_guidance string so generic agents can continue
through the proposal loop instead of treating the denial as terminal.
- Submit. Both proposers POST through the same
SubmitPolicyAnalysispath. Each chunk is persisted with itsanalysis_modefor audit provenance. Agent-authored chunks cannot requestprotocol: tcportls: skip; the sandbox-local API rejects those transport choices for immediate feedback, and the gateway repeats the check before persistence. Omitted-protocol endpoints remain available through the explicit proxy with default TLS termination and HTTP authority checks. Administrators can still author native TCP and raw TLS policy directly. - Build and validate the candidate. The gateway first canonicalizes a mechanistic proposal against the live effective policy. If an endpoint is already governed by an inspected or provider-owned contract, the candidate preserves that contract and adds only the proposed sandbox binary. Provider rules are immutable inputs; the sandbox contribution is stored as an overlay. The gateway then performs the same merge, policy validation, provider composition, credential preflight, and prover evaluation that the candidate would encounter when applied. Each chunk stores the resulting effective candidate, its hashes, any application error, and a review token derived from the candidate and its non-secret live inputs.
- Auto-approval gate (proposer-agnostic, opt-in). Auto-approval fires
only when all three conditions hold: (a)
proposal_approval_moderesolves to"auto"— gateway scope wins, sandbox scope is the per-sandbox override, default is"manual"; (b) the prover delta is empty (prover: no new findings); and (c) the security notes recomputed from the chunk's current proposed rule are empty (see Security-notes gate). Before merging, the gateway reloads the stored chunk and recomputes its candidate from live policy, provider, and credential inputs. If the review token is unchanged, the gateway reuses the persisted prover result. If it changed, the gateway persists the refreshed candidate and requires a fresh review instead of applying it. Decode, prover, merge, provider-composition, or credential failures leave the chunk pending with an application error. The audit event usesCONFIG:APPROVEDand carriesauto=true,source=<mode>,prover_delta=empty, andresolved_from=<gateway|sandbox>as unmapped fields, with message text"auto-approved: no new prover findings"— neversafe. The opt-in gate preserves OpenShell's default-deny posture: with no setting at either scope, every proposal lands inpendingfor human review, even when the prover sees no findings. - Implicit supersede. On any successful submission, the gateway scans
the sandbox's pending chunks for matches on
(host, port, binary)and auto-rejects the older ones with reason"superseded by chunk X". This gives the agent a refinement path (broad mechanistic L4 → narrow agent L7) without an explicitsupersedes_chunk_idfield. - Mechanistic dedup and self-reject. Mechanistic submissions dedup on
(host, port, binary): a repeat denial for an endpoint that already has a draft row folds into that row (bumpinghit_count) instead of creating a new chunk. When the endpoint is already covered by a different approved chunk, the redundant mechanistic submission self-rejects on arrival with reason"already covered by approved chunk X". This only ever acts on a genuinely fresh, still-pendingsubmission; it never rewrites the status of an already-decided chunk. A dedup hit that resolves to an approved row's own id leaves that chunkapprovedand merged — the self-reject path does not un-merge a rule, so flipping an approved chunk torejectedwould leave the governance ledger disagreeing with the still-enforced policy. - Escalation. Anything else lands in
pendingfor human review.
After any successful policy write, pending chunks already covered by the new live effective policy are rejected as redundant. This keeps the review inbox aligned with what the sandbox currently enforces.
Endpoint advisor markers are provenance, not authorization or connection
metadata. Provider- or user-authored endpoints carry explicit provenance;
policy.local endpoints carry advisor provenance. A difference in endpoint
provenance alone is compatible during effective-policy ambiguity validation.
When identical endpoints merge, an explicit declaration dominates an advisor
declaration. Proposal coverage likewise ignores provenance so an approved
overlay converges when an existing explicit declaration already supplies the
same identity.
This compatibility does not weaken SSRF classification. Exact-host trust requires one matching rule to contain both an exact explicit endpoint and a matching binary identity. An advisor endpoint cannot assemble that trust from an unrelated explicit endpoint. When the advisor observes a new binary for an existing explicit endpoint contract, canonicalization keeps the observation in a separate rule whose endpoint retains advisor provenance. A provider or user rule may independently establish trust for its own explicit endpoint and binary pair, but an advisor overlay does not broaden that pair to a different binary.
Security-notes gate
Separately from the prover, each chunk carries advisory security_notes.
Reads, bulk approval, and auto-approval regenerate them from the current
stored proposed rule instead of trusting a persisted value that may be stale
after an edit. Non-empty notes block auto-approval and make
ApproveAllDraftChunks skip the chunk unless include_security_flagged is
set. The chunk stays pending; an explicit human approval can still merge a
flagged chunk.
Private/internal destinations are advisory, not blocking. A literal endpoint
IP, allowed_ips entry, or CIDR intersection in RFC 1918, CGNAT
100.64.0.0/10, IPv6 ULA fc00::/7, or another special-use range covered by
openshell-core net::is_internal_net produces a note. A hostless rule
carrying allowed_ips earns an extra note because it can match any hostname
resolving into the range.
Always-blocked destinations are separate from this advisory classification.
Loopback, link-local, and unspecified IPs/CIDRs, plus localhost and known
metadata endpoint hostnames, are excluded from security notes. Submit and edit
may store such a draft, but existing merge validation rejects it when an
approval attempts to add it to policy; runtime SSRF protections remain the
final enforcement boundary.
Standalone boundary checks
The standalone openshell-prover check command compares a fully composed local
candidate policy with an operator-supplied local boundary. It establishes
Allowed(candidate) ⊆ Allowed(boundary) for the policy domains reported in its
result. It does not fetch gateway state, compose provider rules, apply policy,
or decide whether an in-boundary change is eligible for automatic approval.
The containment model covers filesystem paths, supported process identities,
Landlock compatibility requirements, L4 destinations including IP ranges, and
enforced REST method and path authority. Identity comparisons assume consistent
user and group resolution. Compatibility checks compare requested enforcement
requirements, not the actual kernel state of a running sandbox.
It returns explicit unsupported or inconclusive
results when a sound decision depends on authority or runtime context outside
the model. The result records the covered domains so callers can require the
authority relevant to their decision. The JSON schema_version versions the
result contract, while prover_version identifies the producing implementation.
Before semantic validation, the checker observes cancellation and applies
aggregate limits across both inputs. Oversized checks therefore return
resource_limit without building validation indexes. Cross-protocol ambiguity
validation indexes host and port authority rather than comparing every endpoint
pair, and it checks cancellation while scanning admitted policies.
The Rust containment API has an explicit extensibility contract: options and
modeled-domain evidence permit additive growth, while the four CheckResult
states remain exhaustive and authorization accepts only Within. This Rust
source-compatibility boundary is separate from the CLI JSON schema and the
reported modeled domains. See the openshell-prover crate README for the
supported construction and matching patterns.
This containment operation is separate from the proposal-risk queries below. See the standalone policy prover documentation for installation, command behavior, model limitations, evidence, and exit codes.
What the proposal prover decides
The prover answers four formal questions about each proposed policy
change. Each "yes" answer becomes its own categorical finding — there is
no severity grade. Any finding (of any category) blocks auto-approval.
The categories are intended to be (mostly) mutually exclusive per
underlying change: the gateway suppresses capability_expansion paths
whose (binary, host, port) is also in the credential_reach_expansion
delta, so a brand-new credentialed reach surfaces as one finding rather
than one reach + N method findings.
| Category | The prover detects… |
|---|---|
link_local_reach |
The proposal grants reach to a host in 169.254.0.0/16, fe80::/10, or a known metadata hostname such as metadata.google.internal. Unconditional — cloud-metadata endpoints serve credentials regardless of sandbox state. |
l7_bypass_credentialed |
The proposal lets a binary using a non-HTTP wire protocol (git-remote-https, ssh, nc) reach a host where a sandbox credential is in scope. The L7 proxy cannot inspect the wire protocol; the reviewer decides whether to trust the binary with the credential. |
credential_reach_expansion |
A binary gained credentialed reach to a (host, port) it could not reach before. New authenticated reach is a stated intent change; the reviewer confirms the binary should authenticate to the host at all. |
capability_expansion |
On a (binary, host, port) that already had credentialed reach, the policy adds a new HTTP method. The reviewer sees exactly which method was added (e.g., PUT) and decides if it's part of the agent's task. |
"Credential in scope" is sandbox-coarse, not binary-fine: a credential is
considered in scope if the sandbox has a provider attached whose
target_hosts include the proposed endpoint's host, including runtime-like
first-label wildcard coverage such as *.github.com covering
api.github.com. v1 does not model credential scopes (read-only vs write);
presence is enough.
Proposals intentionally omit allowed_ips. If a proposed rule targets a host
that resolves to a private IP, the proxy's runtime SSRF classification blocks
the connection. The operator must then add an explicit allowed_ips entry to
permit it — a two-step flow that keeps SSRF protection on by default.
The advisor proposes narrow additions and preserves explicit-deny behavior. Auto-approval is gated on prover determinism, not human judgment; an LLM-based contextual reviewer is a deliberate future addition layered on top of the deterministic prover gate.
Security Logging
Sandbox events that represent observable behavior use OCSF structured logs:
| Event | OCSF class |
|---|---|
| Network and proxy decisions | Network or HTTP activity |
| SSH authentication and relay activity | SSH activity |
| Process lifecycle | Process activity |
| Policy and settings changes | Configuration state change |
| Security findings | Detection finding |
Use plain tracing for internal plumbing such as retries, debug state, and intermediate steps where the final observable event is logged separately. Forward-proxy success is a final observable event: emit it only after middleware, token grants, credential rewriting, policy-generation checks, and the HTTP relay have succeeded so a later denial cannot coexist with an allowed record for the same request.
Never log secrets, credentials, bearer tokens, or query parameters in OCSF
messages. OCSF JSONL output may be shipped to external systems.
The gateway-local OCSF JSONL file sink is restricted to the Windows/MXC path
and requires an explicit OPENSHELL_OCSF_JSON=1 opt-in. Other gateway
deployments do not initialize this gateway file sink; a cross-platform gateway
sink requires its own storage and configuration integration.
MXC ETW process events record executable identity but omit command-line
arguments from structured fields and messages. Raw ETW debug summaries replace
the commandLine value with [REDACTED], including pending-buffer eviction
diagnostics.
MXC ETW attribution never treats command text as ownership evidence. It uses the
driver-owned wxc-exec PID plus its kernel process start key as the initial
anchor. ETW attaches that generation key to each record, and the driver queries
the same key from its child process handle. PID attribution requires both values
to match, so reuse cannot transfer ownership between process generations.
Retired PID evidence is discarded; established identity, activity, and
correlation-vector links remain eligible during the five-second late-event
window. Records without matching generation evidence fail closed.