Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
750 changes: 375 additions & 375 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .github/workflows/Versioning.Checks.Job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ jobs:
- name: Check schema is in sync with the Rust wire model (codegen)
run: node scripts/versioning/check-schema-codegen.js

- name: Check PSEC generated contract (provenance + drift)
run: node scripts/versioning/check-psec-codegen.js

- name: Check SDK wire types are in sync with the Rust wire model (codegen)
run: node scripts/versioning/check-sdk-types-codegen.js

Expand Down
30 changes: 27 additions & 3 deletions docs/learning-mode/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,33 @@ surfacing the resulting denials to the caller. Its `mode` selects how each
ungranted access is handled while it is recorded:

> **Host requirement.** `captureDenials` requires a feature-enabled Windows
> build exposing the BaseContainer security-environment and Learning Mode APIs.
> It is not supported by the AppContainer fallback tiers; unsupported hosts
> return `backend_unavailable`.
> build exposing the complete official V2 API set:
> `StartLearningModeTrace`, `StopLearningModeTrace`,
> `CloseLearningModeTrace`, `CreateProcessSecurityEnvironment`,
> `QueryProcessSecurityEnvironmentSupport`, and
> `CloseProcessSecurityEnvironment`. It is not supported by the AppContainer
> fallback tiers; unsupported hosts return `backend_unavailable`.
>
> Internal validation confirmed that build `26657.1002` exposes only the
> incompatible earlier contract and is rejected, while build `26663.1000`
> exposes the complete V2 contract. These are validation points, not a public
> Windows release-floor commitment; callers should rely on the runtime probe.
>
Comment thread
richiemsft marked this conversation as resolved.
> `captureDenials` cannot be combined with `processContainer.leastPrivilege`;
> the Windows process security-environment API used for capture does not expose
> an LPAC token option, so MXC rejects that combination rather than silently
> weakening the requested policy.
>
> `captureDenials` also cannot currently be combined with `network.proxy`.
> The V2 process security-environment proxy contract requires a separate proxy
> AppContainer peer identity; MXC rejects the combination until that peer is
> provisioned by the capture launch path.
>
> `filesystem.deniedPaths` requires
> `QueryProcessSecurityEnvironmentSupport` to advertise
> `PSE_SUPPORT_FS_DENY`. When the bit is absent, capture fails as
> `backend_unavailable`; it cannot fall back to AppContainer or host-DACL
> enforcement.

- `mode: "block"` (default) maps onto `learningModeLogging`
(deny-and-record) β€” the app / user-configurable flow.
Expand Down
57 changes: 51 additions & 6 deletions docs/process-container/os-version-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,49 @@ available bounds what policy can be enforced.
- **T3 (AppContainer + DACL)** is the universal fallback and enforces
filesystem policy via host path ACEs on every release.

## Schema 0.8 process security environment preference

BaseContainer requests using schema versions through 0.7 use the SBOX contract
and the T1/T2/T3 fallback chain above. Schema 0.8 and later prefer the PSEC
process-security-environment contract when its complete export set resolves and
`QueryProcessSecurityEnvironmentSupport` succeeds. During the transition from
the experimental SBOX API to PSEC, an ordinary schema 0.8 request falls back to
SBOX when PSEC is unavailable, then continues through the existing AppContainer
fallback tiers when neither BaseContainer contract is usable.

The PSEC probe requires:

- `CreateProcessSecurityEnvironment`
- `QueryProcessSecurityEnvironmentSupport`
- `CloseProcessSecurityEnvironment`

When `processContainer.captureDenials` is present, fallback is not possible:
capture requires a PSEC handle to key the trace. The host must additionally
expose the complete official V2 Learning Mode export set:

- `StartLearningModeTrace`
- `StopLearningModeTrace`
- `CloseLearningModeTrace`

For capture, unsupported or earlier-contract hosts fail as
`backend_unavailable`. Ordinary ProcessContainer execution still follows the
fallback chain. Internal validation confirmed the earlier contract on build
`26657.1002` is rejected for capture while schema 0.7 SBOX execution remains
functional, and the full V2 contract on build `26663.1000` is accepted. These
builds are validation points, not a public release-floor commitment; runtime
probing is the source of truth.

The PSEC contract cannot represent `processContainer.leastPrivilege`, so
ordinary schema 0.8 requests using that option use the transitional SBOX
contract instead of failing. MXC also does not yet supply the AppContainer peer
identity required by the current model-2 SBOX proxy contract. On hosts with
`Experimental_QuerySandboxSupport`, proxy requests therefore skip
BaseContainer and continue to the AppContainer fallback; older query-less hosts
retain the legacy SBOX proxy path. Similarly, `filesystem.deniedPaths` uses
PSEC only when `QueryProcessSecurityEnvironmentSupport` advertises
`PSE_SUPPORT_FS_DENY`; otherwise MXC continues through the SBOX/AppContainer
fallback chain.

## Filesystem policy

| Aspect | 23H2 | 24H2 | 25H2 | 25H2+ |
Expand All @@ -74,17 +117,19 @@ Notes:
|--------|:--:|:--:|:--:|:--:|
| Capabilities (`internetClient`) | βœ… | βœ… | βœ… | βœ… |
| Firewall rules (`netsh advfirewall`, needs admin) | βœ… | βœ… | βœ… | βœ… |
| Proxy via OS / BaseContainer (`appinfosvc`, FlatBuffer `network_policy.proxy`) | ❌ | ❌ | ❌ | βœ… (T1 only) |
| Proxy | βœ… (AppContainer compatibility) | βœ… (AppContainer compatibility) | βœ… (AppContainer compatibility) | βœ… (legacy T1 or AppContainer compatibility) |

Notes:
- Capability- and firewall-based network enforcement is an AppContainer
primitive and works on every release.
- OS-configured WinHTTP proxy (passed in the FlatBuffer spec to
`CreateProcessInSandbox`) is a T1-only path and therefore 25H2+ only.
- The earlier AppContainer WinHTTP proxy shim (`winhttp-proxy-shim.exe`) is
being retired and is intentionally omitted here: the new WinHTTP cleanup APIs
it depended on are not moving down-level, so it is not a forward-looking
option.
`CreateProcessInSandbox`) is used only on legacy query-less T1 hosts. The
capability-aware model-2 contract requires an AppContainer proxy peer
identity that MXC does not yet author, so those hosts use the AppContainer
compatibility fallback.
- The AppContainer compatibility path uses `winhttp-proxy-shim.exe`. It is not
the forward-looking proxy architecture; support for the model-2 BaseContainer
contract should replace this fallback in a separate change.

## UI restrictions

Expand Down
2 changes: 2 additions & 0 deletions docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ production configs and the dev schema when working on experimental features:
} // dir must already exist; a unique per-run id is stamped
// into the stem (denials.<run-id>.json) and the actual
// path printed on stderr. Omit outputPath for a managed temp file.
// captureDenials cannot be combined with leastPrivilege.
// captureDenials cannot currently be combined with network.proxy.
},

"lxc": { // LXC-specific
Expand Down
93 changes: 93 additions & 0 deletions external/windows-sdk/ProcessSecurityEnvironment.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// FlatBuffers schema for the BaseContainer sandbox specification used in the
// CreateProcessSecurityEnvironment Windows API.
//
// This is the CPSE wire format. The legacy CPIS path continues to use
// SandboxSpec.fbs and its "SBOX" file identifier.
//
// ---- FlatBuffers Schema Evolution Rules ----
//
// Compatible (non-breaking) changes:
// - Add new fields to the END of a table (they get the next vtable slot).
// - Add new tables, structs, enums, or union members.
// - Deprecate a field with (deprecated) - the slot is preserved, readers skip it.
// - Rename a field or table (wire format uses slot indices, not names).
//
// BREAKING changes (will corrupt existing buffers):
// - Remove or reorder fields in a table or struct.
// - Change a field's type (e.g. uint32 -> int32, or scalar -> string).
// - Change a field's default value.
// - Add/remove/reorder values in an enum that is already serialized.
// - Change the file_identifier or root_type.
// - Change a field from scalar to non-scalar (or vice versa).
//
// To machine-check for breaking changes, keep a copy of the last shipped schema
// (e.g. ProcessSecurityEnvironment.previous.fbs) and run:
//
// flatc --conform ProcessSecurityEnvironment.previous.fbs ProcessSecurityEnvironment.fbs
//
// --conform verifies that every field/enum/table in the old schema still exists
// at the same vtable slot, type, and default in the new schema.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This schema is copied from the official OS PSEC schema, including the FlatBuffer evolution guidance, so I retained it to keep the checked-in contract aligned with its source. If you intended a different specific section on line 32 to be removed, could you clarify which portion?


namespace ProcessSecurityEnvironmentLayout;

struct SchemaVersion {
major:uint16;
minor:uint16;
}

table ProcessSecurityEnvironment {
version:SchemaVersion (required);
capabilities:string;
disallow_win32k_system_calls:bool = false;
ui_restrictions:uint64 = 0;
fs_read_write:[string];
fs_read_only:[string];
fs_deny:[string];
network_policy:NetworkPolicy;
}

table ProxyInfo {
url:string;
}

enum FilterAction : byte { deny, allow }
enum IpProtocol : byte { any, tcp, udp, icmpv4, icmpv6 }

table IpSubnet {
address:string;
prefix_length:ubyte = 0;
}

table DestinationRule {
subnet:IpSubnet;
except:[IpSubnet];
}

table PortRule {
protocol:IpProtocol = any;
port:uint16 = 0;
end_port:uint16 = 0;
}

table EndpointRule {
destinations:[DestinationRule];
ports:[PortRule];
}

table EndpointPolicy {
default_action:FilterAction = deny;
allow:[EndpointRule];
deny:[EndpointRule];
}

table NetworkPolicy {
proxy:ProxyInfo;
egress:EndpointPolicy;
allowed_appcontainer_peer:string;
}

root_type ProcessSecurityEnvironment;
file_identifier "PSEC";
52 changes: 52 additions & 0 deletions external/windows-sdk/ProcessSecurityEnvironment.provenance.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Provenance for the vendored ProcessSecurityEnvironment FlatBuffers schema.
#
# This file is the single source of truth for the pinned regeneration toolchain
# and the authoritative hash of the vendored `ProcessSecurityEnvironment.fbs`.
# It is consumed by:
# * src/core/generated/process_security_environment_specification/regenerate.ps1
# (validates the schema hash + pins the exact flatc version before generating)
# * scripts/versioning/check-psec-codegen.js
# (CI drift gate β€” verifies the committed schema + generated crate)
#
# Update this file whenever the vendored schema is refreshed from the OS source,
# then regenerate the bindings (see the crate README).

[source]
# The schema originates from the internal Microsoft Windows OS repository and is
# not publicly redistributable. Only the schema text (not the OS tree) is vendored.
repository = "Windows OS (internal Azure DevOps)"
# The PSEC schema is a Windows OS containment contract; there is no public OS
# revision to cite. Instead of guessing a source path/revision, we record the
# Windows build the vendored schema contract was validated against.
validated_windows_build = "10.0.26663.1000"
# Azure DevOps PR 16307987 is the RELATED Learning Mode trace ABI change β€” it is
# NOT the pull request that introduced or last modified this PSEC schema.
# Recorded for traceability only; do not represent it as the schema's origin.
related_trace_abi_pull_request = 16307987

[schema]
# SHA-256 of external/windows-sdk/ProcessSecurityEnvironment.fbs, computed over
# the LF-normalized (git-blob) content so it is checkout-independent regardless
# of autocrlf. Verified by regenerate.ps1 and the CI drift gate.
sha256 = "7d14b01850a735329da00cde4d4d2e32e463f49026a39708be9059fd64e764d3"

[tool]
# Exact flatc version used to generate the committed bindings. Regeneration
# pins this exact version (not a floor) so output is byte-reproducible.
# 25.12.19 is the first release carrying flatbuffers PR #8709, which stops flatc
# emitting elided lifetimes that trip the `mismatched_lifetime_syntaxes` lint
# (added in Rust 1.89).
flatc_version = "25.12.19"
flatc_release = "https://github.com/google/flatbuffers/releases/tag/v25.12.19"
generated_date = "2026-08-03"

# Exact GitHub release assets for flatc 25.12.19. The CI drift gate downloads the
# platform asset, verifies its SHA-256 against these values, unzips it, and
# regenerates into a temp directory to diff against the committed bindings.
[tool.flatc_assets.linux]
name = "Linux.flatc.binary.clang++-18.zip"
sha256 = "50c1915deeeb714f2a05c8ec795bd1af898d251a62e2774067703b29188efc90"

[tool.flatc_assets.windows]
name = "Windows.flatc.binary.zip"
sha256 = "fff9445c9db907227bc64b54cc98743084c4949282aa4e576cff6a955724ddc8"
4 changes: 2 additions & 2 deletions schemas/dev/mxc-config.schema.0.8.0-dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"CaptureDenials": {
"additionalProperties": false,
"description": "Windows denial-capture settings. The presence of the `captureDenials` object enables capture; all fields are optional.",
"description": "Windows denial-capture settings. The presence of the `captureDenials` object enables capture; all fields are optional. Capture is incompatible with `processContainer.leastPrivilege` and `network.proxy`. Explicit `filesystem.deniedPaths` requires the host's V2 process security-environment support query to advertise native deny enforcement.",
"properties": {
"mode": {
"anyOf": [
Expand Down Expand Up @@ -699,7 +699,7 @@
"type": "null"
}
],
"description": "Windows denial capture. When present, the runner records the sandboxed process's access attempts to a learning-mode ETL trace for later inspection. Requires a host that exposes the learning-mode OS API."
"description": "Windows denial capture. When present, the runner records the sandboxed process's access attempts to a learning-mode ETL trace for later inspection. Requires a host that exposes the complete official V2 Learning Mode and process security-environment API set. Cannot be combined with `leastPrivilege` or `network.proxy`; `filesystem.deniedPaths` additionally requires the V2 deny-support capability."
},
"learningMode": {
"description": "AppContainer learning mode (deny-and-record): failed access checks are logged for diagnostics while the accesses stay denied; containment is unchanged. Distinct from the allow-all `permissiveLearningMode` capability, which is injected internally by the `--audit` CLI flag or dedicated denial-capture configuration.",
Expand Down
Loading
Loading