|
| 1 | +# Learning-mode capabilities |
| 2 | + |
| 3 | +MXC sandboxes are **deny-by-default**: when a workload touches a file, registry |
| 4 | +key, or other resource the policy does not grant, the access is blocked and the |
| 5 | +OS returns the usual "Access is denied" error. For non-trivial workloads this is |
| 6 | +operationally fragile — the author must enumerate every path the workload will |
| 7 | +ever touch up front, or hand the operator a stack trace and ask them to guess. |
| 8 | + |
| 9 | +**Learning mode** turns those denied accesses into observable events. It is |
| 10 | +enabled per-run through two Windows-specific policy capabilities. These |
| 11 | +capabilities are the *inputs* to learning mode; the machinery that collects and |
| 12 | +surfaces the resulting denial events is layered on top in later work. |
| 13 | + |
| 14 | +> **Platform support.** Learning-mode capabilities are **Windows-only** and |
| 15 | +> apply to the AppContainer-based backends (classic AppContainer and |
| 16 | +> BaseContainer, which share `backends/appcontainer/common`). On other platforms |
| 17 | +> the capability strings are ignored. |
| 18 | +
|
| 19 | +## The two capabilities |
| 20 | + |
| 21 | +The two capabilities are **semantically distinct and must not be conflated**: |
| 22 | + |
| 23 | +| Capability | Behavior | Enforcement | |
| 24 | +| ----------------------- | ----------------------------------------------------- | ------------------------------------ | |
| 25 | +| `learningModeLogging` | Logs every **failed** access check (deny-and-record). | **Unchanged** — accesses stay denied. | |
| 26 | +| `permissiveLearningMode`| Logs **every** access check and **allows** it (audit / allow-all). | **Weakened** — the container no longer enforces deny-by-default. | |
| 27 | + |
| 28 | +### `learningModeLogging` — deny-and-record |
| 29 | + |
| 30 | +The OS records each access check that *would have been denied*, but the access |
| 31 | +is **still denied**. Containment is unchanged, so this is safe to use as a |
| 32 | +diagnostic aid: the workload behaves exactly as it would without learning mode, |
| 33 | +while producing a record of what it tried and failed to reach. |
| 34 | + |
| 35 | +### `permissiveLearningMode` — audit / allow-all |
| 36 | + |
| 37 | +The OS records **every** access check and **allows** it. This is an audit mode: |
| 38 | +it answers "what would this workload touch if nothing were blocked?" but it does |
| 39 | +so by **not enforcing deny-by-default** for the duration of the run. |
| 40 | + |
| 41 | +Because it relaxes containment, `permissiveLearningMode` is **security-sensitive**: |
| 42 | +whenever it is present, both the AppContainer and BaseContainer runners emit an |
| 43 | +always-visible **security warning** on the host's stderr. In-process Rust callers |
| 44 | +can also inspect it through `Sandbox::warnings()` or `Output::warnings`. It is a |
| 45 | +reserved internal capability enabled by the dedicated audit/capture entry points. |
| 46 | + |
| 47 | +The parser rejects both learning-mode capability names in |
| 48 | +`processContainer.capabilities`, case-insensitively. This prevents a policy from |
| 49 | +selecting contradictory modes or bypassing the security-sensitive entry points. |
| 50 | + |
| 51 | +## How to enable them |
| 52 | + |
| 53 | +Enable deny-and-record through the dedicated `learningMode` setting: |
| 54 | + |
| 55 | +```jsonc |
| 56 | +{ |
| 57 | + "processContainer": { |
| 58 | + "learningMode": true |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +Enable permissive audit mode through the CLI: |
| 64 | + |
| 65 | +```text |
| 66 | +wxc-exec --audit --config <config> |
| 67 | +``` |
| 68 | + |
| 69 | +These entry points inject the reserved capability strings internally; users |
| 70 | +must not add them directly to `processContainer.capabilities`. |
| 71 | +When either learning-mode capability is in effect the runner emits a diagnostic |
| 72 | +describing the mode (informational logging for `learningModeLogging`, an |
| 73 | +always-visible stderr security warning for `permissiveLearningMode`). |
| 74 | + |
| 75 | +## Three learning-mode flows |
| 76 | + |
| 77 | +Learning-mode telemetry is consumed through three distinct flows. They differ in |
| 78 | +*who* runs them, *how* the capability is supplied, and *whether* deny-by-default |
| 79 | +stays enforced: |
| 80 | + |
| 81 | +| Flow | Audience | Entry point | Enforcement | |
| 82 | +| ---- | -------- | ----------- | ----------- | |
| 83 | +| **Developer inner-loop** | The author bringing a workload up | `--audit` CLI flag | Relaxed (allow-all) | |
| 84 | +| **App / user-configurable** | Apps that let end users tune their own config | `captureDenials` (`mode: "block"`) / `learningModeLogging` | Enforced (deny-and-record) | |
| 85 | +| **Fleet auditing** | IT admins | `captureDenials` (`mode: "allow"`) / `permissiveLearningMode` | Relaxed (allow-all) | |
| 86 | + |
| 87 | +1. **Developer inner-loop (`--audit`).** A developer runs `wxc-exec --audit` |
| 88 | + with ProcessContainer containment to discover the capabilities and paths |
| 89 | + their process needs. `--audit` is rejected for every other Windows backend. |
| 90 | + It triggers UAC, injects `permissiveLearningMode`, and drives a WPR/ETW |
| 91 | + permissive-learning-mode trace for the run. This is typically a static |
| 92 | + config the developer iterates on locally. |
| 93 | + |
| 94 | + ``` |
| 95 | + wxc-exec --audit --config <config> |
| 96 | + ``` |
| 97 | + |
| 98 | +2. **App / user-configurable (`captureDenials` block / `learningModeLogging`).** |
| 99 | + An app wants to let its users "configure" their own sandbox. Each user |
| 100 | + workflow differs, so the app records what was blocked, presents it through its |
| 101 | + own UX, and re-generates the config with the new paths/capabilities. |
| 102 | + Deny-by-default stays enforced — the workload behaves exactly as it would in |
| 103 | + production while the denials are recorded. |
| 104 | + |
| 105 | +3. **Fleet auditing (`captureDenials` allow / `permissiveLearningMode`).** |
| 106 | + IT admins audit access checks across a fleet by running MXC instances in |
| 107 | + permissive learning mode. This flow does **not** trigger UAC: the capability |
| 108 | + is supplied through config and takes effect directly, allowing and recording |
| 109 | + every access check. |
| 110 | + |
| 111 | +## Relationship to denial capture |
| 112 | + |
| 113 | +Injecting these capabilities makes the OS *emit* learning-mode events. The |
| 114 | +Windows-only `captureDenials` config switch drives collecting those events and |
| 115 | +surfacing the resulting denials to the caller. Its `mode` selects how each |
| 116 | +ungranted access is handled while it is recorded: |
| 117 | + |
| 118 | +- `mode: "block"` (default) maps onto `learningModeLogging` |
| 119 | + (deny-and-record) — the app / user-configurable flow. |
| 120 | +- `mode: "allow"` maps onto `permissiveLearningMode` (allow-and-record) |
| 121 | + — the fleet-auditing flow. |
| 122 | + |
| 123 | +The capture pipeline is delivered incrementally and is documented separately as |
| 124 | +it lands. |
0 commit comments