Skip to content
Open
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
19 changes: 16 additions & 3 deletions docs/learning-mode/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,19 @@ The pointer echoes the file's `summary`; the authoritative record is the file
itself. In-process Rust callers receive the same information through
`Output::output_metadata` or `Sandbox::output_metadata()` after waiting. The
C# SDK exposes it through `RunResult.OutputMetadata` and
`MxcSandboxProcess.OutputMetadata`. The intermediate ETW `.etl` trace is an
internal, runner-managed temp file that MXC decodes and then deletes β€” callers
never see it.
`MxcSandboxProcess.OutputMetadata`.

By default, the intermediate ETW `.etl` trace is an internal, runner-managed
temp file that MXC deletes after analysis. Set `captureDenials.retainEtl` to
`true` to preserve the sealed trace for diagnostics. When retention succeeds,
the structured pointer and in-process metadata include its absolute
`etlPath`:

```json
{"type":"captureDenials","outputPath":"C:\\logs\\denials.4321_0123456789abcdef0123456789abcdef.json","exitCode":0,"totalDenials":2,"deniedResourcesTruncated":false,"etlPath":"C:\\Users\\runneradmin\\AppData\\Local\\Temp\\mxc_capture_denials_4321_0123456789abcdef0123456789abcdef.etl"}
```

If analysis fails while retention is enabled, MXC preserves the ETL and
includes its path in the returned error. ETL traces can contain sensitive
resource paths and identifiers; callers that retain them are responsible for
restricting access and deleting them when they are no longer needed.
10 changes: 6 additions & 4 deletions docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ production configs and the dev schema when working on experimental features:
// is logged (deny-by-default preserved). "allow":
// access is allowed and logged (audit; relaxes
// deny-by-default, emits a security warning).
"outputPath": "C:\\logs\\denials.json" // JSON denials file the app reads. The parent
} // 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.
"outputPath": "C:\\logs\\denials.json", // JSON denials file the app reads. Parent dir
// must exist; a unique per-run id is stamped into the
// stem and the actual path is printed on stderr.
"retainEtl": false // Keep the sealed ETL after analysis and report its
// path in output metadata. Defaults to false.
} // Omit outputPath for a managed temp file.
// captureDenials cannot be combined with leastPrivilege.
// captureDenials cannot currently be combined with network.proxy.
},
Expand Down
9 changes: 8 additions & 1 deletion schemas/dev/mxc-config.schema.0.8.0-dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,18 @@
"description": "How each ungranted access check is handled while it is recorded. Both modes log every access the policy does not grant to the ETL trace; the mode only decides whether that access is blocked or allowed. Defaults to `block` when omitted."
},
"outputPath": {
"description": "Absolute path where the JSON denials output file is written β€” the deliverable a consuming application reads to learn what the workload was denied. It is a single JSON document `{ \"denials\": [...], \"summary\": {...} }`. A per-run identifier (process id plus random suffix) is inserted into the file stem (e.g. `denials.json` -> `denials.<run-id>.json`) so concurrent and sequential captures do not collide; the actual path is reported on stderr. When omitted, MXC writes it to a managed per-run temporary file and prints its path on stderr. The parent directory must already exist. (The intermediate ETL trace is an internal, runner-managed temp file that is decoded then deleted.)",
"description": "Absolute path where the JSON denials output file is written β€” the deliverable a consuming application reads to learn what the workload was denied. It is a single JSON document `{ \"denials\": [...], \"summary\": {...} }`. A per-run identifier (process id plus random suffix) is inserted into the file stem (e.g. `denials.json` -> `denials.<run-id>.json`) so concurrent and sequential captures do not collide; the actual path is reported on stderr. When omitted, MXC writes it to a managed per-run temporary file and prints its path on stderr. The parent directory must already exist. (The intermediate ETL trace is an internal, runner-managed temp file.)",
"type": [
"string",
"null"
]
},
"retainEtl": {
"description": "Keep the sealed ETL trace after analysis and report its path in output metadata. Defaults to `false`, which deletes the trace after analysis. Retained traces can contain sensitive resource paths and identifiers; callers are responsible for securing and deleting them.",
"type": [
"boolean",
"null"
]
}
},
"type": "object"
Expand Down
20 changes: 20 additions & 0 deletions sdk/dotnet/Microsoft.Mxc.Sdk.Tests/MxcSandboxTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,24 @@ public void SandboxPolicy_SerializesToCamelCaseJson()
Assert.Equal("read", root.GetProperty("ui").GetProperty("clipboard").GetString());
Assert.True(root.GetProperty("ui").GetProperty("allowWindows").GetBoolean());
}

[Fact]
public void CaptureDenialsOutput_DeserializesRetainedEtlPath()
{
const string json = """
{
"type": "captureDenials",
"outputPath": "denials.json",
"exitCode": 0,
"totalDenials": 1,
"deniedResourcesTruncated": false,
"etlPath": "capture.etl"
}
""";

var output = JsonSerializer.Deserialize<CaptureDenialsOutput>(json);

Assert.NotNull(output);
Assert.Equal("capture.etl", output.EtlPath);
}
}
4 changes: 4 additions & 0 deletions sdk/dotnet/Microsoft.Mxc.Sdk/SandboxOutputMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ public sealed class CaptureDenialsOutput
/// <summary>Whether the emitted denial set was truncated.</summary>
[JsonPropertyName("deniedResourcesTruncated")]
public bool DeniedResourcesTruncated { get; init; }

/// <summary>Absolute path to the retained ETL trace, when requested.</summary>
[JsonPropertyName("etlPath")]
public string? EtlPath { get; init; }
}
3 changes: 2 additions & 1 deletion sdk/dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ catch (MxcException ex)
thread pool. `MxcSandbox.NativeVersion` returns the loaded `mxc_ffi` version.
Optional feature outputs are returned through `RunResult.OutputMetadata`; for
`captureDenials`, `OutputMetadata.CaptureDenials.OutputPath` identifies the
generated JSON document and carries its summary.
generated JSON document and carries its summary. When ETL retention is enabled,
`OutputMetadata.CaptureDenials.EtlPath` identifies the retained trace.

### Streaming

Expand Down
6 changes: 5 additions & 1 deletion sdk/node/src/generated/wire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ export interface CaptureDenials {
*/
mode?: CaptureDenialsMode | null;
/**
* Absolute path where the JSON denials output file is written β€” the deliverable a consuming application reads to learn what the workload was denied. It is a single JSON document `{ "denials": [...], "summary": {...} }`. A per-run identifier (process id plus random suffix) is inserted into the file stem (e.g. `denials.json` -> `denials.<run-id>.json`) so concurrent and sequential captures do not collide; the actual path is reported on stderr. When omitted, MXC writes it to a managed per-run temporary file and prints its path on stderr. The parent directory must already exist. (The intermediate ETL trace is an internal, runner-managed temp file that is decoded then deleted.)
* Absolute path where the JSON denials output file is written β€” the deliverable a consuming application reads to learn what the workload was denied. It is a single JSON document `{ "denials": [...], "summary": {...} }`. A per-run identifier (process id plus random suffix) is inserted into the file stem (e.g. `denials.json` -> `denials.<run-id>.json`) so concurrent and sequential captures do not collide; the actual path is reported on stderr. When omitted, MXC writes it to a managed per-run temporary file and prints its path on stderr. The parent directory must already exist. (The intermediate ETL trace is an internal, runner-managed temp file.)
*/
outputPath?: string | null;
/**
* Keep the sealed ETL trace after analysis and report its path in output metadata. Defaults to `false`, which deletes the trace after analysis. Retained traces can contain sensitive resource paths and identifiers; callers are responsible for securing and deleting them.
*/
retainEtl?: boolean | null;
}

/**
Expand Down
Loading
Loading