Permissive Learning Mode 2/6 filesystem extraction#585
Merged
Conversation
lilybarkley-msft
force-pushed
the
user/lilybarkley/plm-pr2-fs-extraction
branch
3 times, most recently
from
July 2, 2026 01:53
2d73df4 to
ebc5420
Compare
lilybarkley-msft
force-pushed
the
user/lilybarkley/plm-pr1-audit-skeleton
branch
from
July 7, 2026 01:50
cdd12e8 to
673a3bc
Compare
lilybarkley-msft
force-pushed
the
user/lilybarkley/plm-pr2-fs-extraction
branch
2 times, most recently
from
July 7, 2026 21:37
87f340d to
80667ed
Compare
Contributor
Author
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
lilybarkley-msft
force-pushed
the
user/lilybarkley/plm-pr2-fs-extraction
branch
from
July 8, 2026 01:24
c39b649 to
3d9c67a
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds the filesystem-extraction slice of Permissive Learning Mode (PLM): capturing ETW/WPR traces, decoding EventID=14 access-failures into file-path + access-mask findings, and merging those findings into filesystem.{readwritePaths, readonlyPaths}. It also wires PLM into wxc-exec --audit (Windows-only), including elevated plm.exe spawning and lifecycle coordination/cleanup.
Changes:
- Introduces the PLM crate (
plm.exe+ library) with WPR profile staging, start/stop/log flows, ETW XML walking, and access-failure decoding. - Implements filesystem path normalization + filtering and merges resulting access events into MXC config JSON.
- Adds
wxc-exec --auditintegration: injectpermissiveLearningMode, spawn elevatedplm.exe start/stop, and coordinate cleanup via shared primitives.
Reviewed changes
Copilot reviewed 28 out of 30 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/host/plm/src/wpr_path.rs | Safe absolute-path resolution for wpr.exe (avoids CWD/PATH/env spoof). |
| src/host/plm/src/stop.rs | plm stop flow: stop trace, parse ETL, merge FS findings into config in-memory. |
| src/host/plm/src/start.rs | plm start WPR trace start + cancel-and-retry logic, plus failure replay. |
| src/host/plm/src/profile_gen.rs | Embeds/stages plm.wprp next to the executable with atomic write. |
| src/host/plm/src/main.rs | Windows-only CLI entrypoint with singleton + ctrl-handler + subcommand dispatch. |
| src/host/plm/src/log.rs | Interactive plm log start/stop and blank-config merge preview. |
| src/host/plm/src/lib.rs | Exposes PLM modules; gates Windows-only modules. |
| src/host/plm/src/event_parser.rs | ETW event enumeration/rendering + parse accumulator + dispatch plumbing. |
| src/host/plm/src/coordination.rs | Shared singleton, ctrl-handler wait helper, and bypass signaling. |
| src/host/plm/src/config.rs | JSON config load + filesystem path normalization/filtering + merge logic. |
| src/host/plm/src/access_failure.rs | EventID=14 decode: normalization + filters + per-event accumulation. |
| src/host/plm/src/access_event.rs | LearningModeAccessEvent data model. |
| src/host/plm/readme.md | PLM documentation (how it works, CLI, layout, limitations). |
| src/host/plm/Cargo.toml | Adds new plm crate (deps + windows-gated deps + build script). |
| src/host/plm/build.rs | Embeds version info + admin manifest for release Windows builds. |
| src/core/wxc/src/plm_launch.rs | UAC-elevated spawning wrapper for plm.exe with captured stdio. |
| src/core/wxc/src/main.rs | Adds --audit/--audit-verbose, audit lifecycle wiring, and cleanup integration. |
| src/core/wxc/src/audit.rs | Implements --audit lifecycle state, cleanup guards, and plm invocation. |
| src/core/wxc/Cargo.toml | Adds plm + tempfile dependencies for audit integration. |
| src/core/wxc_common/src/models.rs | Adds ExecutionRequest.audit to carry --audit intent. |
| src/core/wxc_common/src/config_parser.rs | Adjusts permissiveLearningMode handling/logging in debug builds; initializes audit=false. |
| src/Cargo.toml | Adds host/plm workspace member; adds shared deps (chrono, roxmltree). |
| src/Cargo.lock | Locks new plm and roxmltree deps; adds tempfile dep to wxc. |
| src/backends/appcontainer/common/src/appcontainer_runner.rs | Enforces permissiveLearningMode in release only when request.audit is set. |
| sdk/tests/integration/test-helpers.ts | Allows plm.exe as an optional packaged binary. |
| sdk/tests/integration/package-lock.json | Updates integration test lockfile versions for local SDK link. |
| README.md | Documents wxc-exec --audit and links to PLM docs with release-build warning. |
| build.bat | Builds/stages plm.exe, adds --with-bfs, and forces SDK integration test relink. |
| .github/workflows/Build.Linux.Job.yml | CI gate to build/test plm cross-platform (stub + portable modules). |
| .azure-pipelines/templates/Rust.Build.Job.yml | ADO CI runs cargo test -p plm on Windows x64. |
Files not reviewed (1)
- sdk/tests/integration/package-lock.json: Generated file
pr1 was merged upstream without these follow-up fixes, so they move here (pr2) instead: - Deduplicate wpr path resolution: audit.rs now uses plm::wpr_path::wpr_command - Deduplicate wpr stop: log.rs now uses stop::stop_plm_trace_with - Fix run_plm_command docstring to describe capture+conditional replay - Remove orphaned plm.exe docstring from wxc main.rs import - Rewrite plm-dep comment in wxc Cargo.toml to reflect actual imports - Fix readme link: base-process-container/ -> process-container/ - Remove out-of-scope Stop options (bin_path, adjusted_config_path, verbose_logging) that pr1 was carrying prematurely; pr2's own filesystem-extraction commit reintroduces them where they belong. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Walks EventID=14 records from the captured .etl, decodes file paths through normalization + post-XPath filters, and merges them into ilesystem.readwritePaths / ilesystem.readonlyPaths on an in-memory copy of the input config. The Adjusted_*.json writer arrives in the next PR. New modules: - event_parser: EvtQuery/EvtRender walk + ParseAccumulator dispatch - access_failure: EventID=14 decoder + path normalization - access_event: LearningModeAccessEvent plain struct - config: WRITE/READ masks, filesystem init, update_from_access_events Wired stop.rs and log.rs to invoke the FS merge pipeline. Capability ACE-blob extraction, EventID=27 UI relaxation, the adjusted-config writer, and merge_capabilities arrive in subsequent PRs. 37 tests pass; cargo fmt + clippy clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the `std::fs::copy(config_path, &dest_config)` above the
parse.is_empty() early return so operators always have a
{trace.etl, <input>.json} pair in the log directory, even when
the parse yielded nothing mergeable.
Previously the config copy sat below the bail-out, so an empty
parse left a bare trace.etl in log_dir with no way for the
operator to correlate the trace with the input that produced it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Load the source config into memory BEFORE the copy-to-log_dir
side effect, and edit the pre-loaded Value rather than
re-reading dest_config after the copy.
This eliminates a Windows read-after-write hazard in the
previous flow (copy source -> dest_config, then load_config on
dest_config): an AV filter can occasionally serve a stale or
empty buffer for a file that std::fs::copy just wrote, producing
intermittent 'failed to parse JSON' errors that manifested as
test instability under load.
Reordering also gives the operator a stronger invariant: if
plm.exe reached the point where it would touch log_dir, the
input snapshot is guaranteed on disk before any edit is
attempted, so an interrupted run always leaves a coherent
{trace.etl, <input>.json} pair rather than a bare trace.etl
next to a half-loaded config.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lilybarkley-msft
force-pushed
the
user/lilybarkley/plm-pr2-fs-extraction
branch
from
July 8, 2026 02:25
3d9c67a to
a4308f6
Compare
MGudgin
reviewed
Jul 8, 2026
MGudgin
reviewed
Jul 8, 2026
MGudgin
reviewed
Jul 8, 2026
MGudgin
reviewed
Jul 8, 2026
MGudgin
reviewed
Jul 8, 2026
- Fix config.rs module doc: capability-merge and Adjusted_*.json writer arrive in later PRs, not pr2 - Fix resolve_bin_path docstring to describe self-access filter consumption (was: 'exposed for tests only') - Fix readme: --config-path drives filesystem merge today (was described as fully deferred); log interactive mode is implemented - Restore stop --bin-path / --adjusted-config-path / --verbose-logging clap args now that pr1 (correctly) does not carry them Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- config.rs self-access filter comment: reword 'wxc-exec binary' to 'audited application binary'; bin_path is the --bin-path arg (or the plm.exe dir fallback), not wxc-exec's own path. - config.rs parent_for_write verbose branch: message was a stale PowerShell-parser artifact claiming 'Only files and directories are currently supported'. In Rust the branch only fires when the path has no final component; reword to reflect that. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1) event_parser::render_event_xml: change backing buffer from Vec<u8> to Vec<u16> so the *const u16 cast for from_raw_parts is always properly aligned (was UB even though it worked on x86). Treat EvtRender's BufferSize/BufferUsed as byte counts explicitly at the Win32 boundary. 2) stop::WprExeStopper::stop: pass the trace_file Path to wpr -stop directly via Command::arg(&Path) instead of round-tripping through to_string_lossy() (which would silently swap in U+FFFD for non-Unicode path bytes). 3) wpr_path::resolve_wpr_path: fix truncation check to use >= buf.len() per GetSystemDirectoryW's Win32 contract, and retry once with the required size rather than hardcoding a C:\\Windows\\System32\\wpr.exe fallback (which was wrong on any non-C: Windows install). Return Option<PathBuf> and cache it. 4) wpr_path: rename verify_wpr_signed -> verify_wpr_present. The function only checks is_file(); the old name suggested a signature verification we deliberately do not perform (see the module doc for the security rationale). Update the caller in main.rs. 5,6) config_parser::merge_appcontainer_config: replace debug-only eprintln! calls in the learningMode and permissiveLearningMode paths with logger.log(...) so the wxc_common library layer stops unconditionally writing to stderr. The CLI already installs a stderr-mirroring Logger; embedders that don't want the stderr side effect can now suppress it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1) src/host/plm/src/main.rs: convert stored blob back to CRLF to
match the file's line-ending convention in origin/main; earlier
edits inadvertently rewrote it as LF, making the PR diff show a
whole-file rewrite.
2) event_parser::render_event_xml: replace unsafe { buf.set_len(0) }
with buf.clear() — for Vec<u16> there is nothing to drop and the
safe API expresses intent more clearly.
4) event_parser::render_event_xml: fix growth calculation on the
ERROR_INSUFFICIENT_BUFFER retry. Vec::reserve(additional)
guarantees capacity >= len + additional, and len is 0 here
(buf was cleared), so passing (needed_u16 - buf.capacity())
under-reserved when needed_u16 was less than 2*capacity and
still under-reserved otherwise. Pass needed_u16 directly.
Reverted (was pr2 commit): --with-bfs build.bat additions; the
AppContainer Tier 2 BFS build flag will be re-introduced
separately per reviewer request.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lilybarkley-msft
force-pushed
the
user/lilybarkley/plm-pr2-fs-extraction
branch
from
July 8, 2026 19:33
a4308f6 to
08dbc86
Compare
MGudgin
reviewed
Jul 10, 2026
…mmary, SAFETY Re-applies the PLM review fixes at the base of the stack (pr2), where the review threads live: - event_parser: named EVENT_ID_ACCESS_FAILURE/EVENT_ID_UI_VIOLATION consts used in the XPath query and dispatch [14]; skip individual unrenderable events (verbose-logged) instead of aborting the whole trace [21]; share wxc_common::string_util::to_wide instead of a local to_wide_z [5]; add SAFETY comments to every unsafe ETW block (EvtClose/EvtQuery/EvtNext/ EvtRender x2/GetLastError/set_len) [4][7]; add seen_access_events dedup set to ParseAccumulator [15]. - access_failure: drop duplicate (access_mask, path) access failures via seen_access_events [15]; fix self-access drive-prefix boundary get(3..) -> get(2..) so the leading path character is preserved [17]. - config: remove redundant seen_rw/seen_ro (the existing-set inserts already dedup) [26]; gate write_added_paths_summary stdout behind verbose [22]. - stop/log: thread verbose into write_added_paths_summary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
- [2] wpr_path.rs: correct doc comment "WPT not present" -> "WPR not present" (the resolved binary is wpr.exe / Windows Performance Recorder). - [13] event_parser.rs: spell out abbreviated locals in is_skippable (nb->normalized_bytes, cwd_b->cwd_bytes, pb->prefix_bytes, p->prefix, cwd_eq->cwd_equals, closure a/b->lhs/rhs) for readability. - [20] readme.md: the blank-config "diff" preview is implemented in this PR (log.rs), so drop the stale "arrives in later PRs" note and describe it. - [21] event_parser.rs: count and surface malformed event records instead of silently swallowing them. Add ParseAccumulator::parse_failures, increment in consume() when parse_event_xml returns None (verbose per-record warning), and emit an end-of-parse summary in into_result. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
Move the 32-test block covering normalize_path, is_drive_root, path_starts_with_any_norm, update_from_access_events, initialize_filesystem, and load_config into pr2, the PR that introduces config.rs and these functions. Previously these tests lived in pr5 (and were briefly relocated to pr3); pr2 is the earliest branch where the code under test exists and the tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
Fixes for the open review comments on PR #585: - access_failure.rs: deduplicate access failures by (case-insensitive) path and OR their access masks into a single entry, instead of keeping one near-identical row per (mask, path). A file read then written now collapses to one read+write entry, and long traces no longer balloon valid_access_events with redundant rows. - access_failure.rs: replace the self-access `ends_with` suffix test with an exact volume-relative comparison via a new volume_relative_path helper, fixing false positives that silently dropped genuine events (e.g. a decoy C:\app.exe matching the real ...\Tools\app.exe, or any short path matching every .exe). - config.rs: cache positive prefix matches in the readwrite/readonly short-circuits so repeated events for an already-covered path hit the O(1) set lookup instead of re-scanning the grant vector. - config.rs: rebuild normalize_path into a single pre-allocated String buffer instead of a Vec<String> + join, cutting per-component heap allocations on the hot path while preserving join semantics exactly. - access_failure.rs: add dispatcher end-to-end, self-access exact-match (incl. decoy regression), dedup/mask-merge, and volume_relative_path unit tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
Addresses bbonaby's review question on config.rs: the readwrite branch in `update_from_access_events` refuses to emit a bare drive root (which would grant the entire volume), but the readonly branch had no such guard. The upstream `is_skippable` `len < 4` filter drops the common `C:` / `C:\` spellings, but a root that reaches the read branch in a longer form (e.g. `C:\\`, which `trim_backslashes_in_place` collapses to `C:`, or a verbatim `\\?\C:\`) would be added to `readonlyPaths` verbatim, over-granting read of the whole volume. Mirror the write-branch `is_drive_root` guard on the read branch and add regression tests (bare, doubled-separator, and verbatim roots must not be granted; a real file under the root still is). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
Three mechanical review fixes on PR #585: - Build the EventID XPath filter with the compile-time `w!` macro instead of `format!` + `to_wide` on every call, with a `const` assertion keeping the literal in sync with the EVENT_ID_* constants. (r3575635072) - Drop the dead `current_directory` field (and now-unused lifetime) on `ParseAccumulator`: only the constructor parameter is used, to derive the cached lowercase CWD forms. (r3576522117) - Remove the stale `#[allow(dead_code)]` on `ParsedEvent::event_id`; the field is read by the event-id dispatch in `consume`. (r3576531351) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
Extract the native EvtQuery/EvtNext/EvtRender walk in `for_each_event_xml` behind an `EtwEventSource` trait + a platform-independent `drive_event_stream` driver, and split the render buffer-sizing arithmetic into pure helpers. This lets the loop's real behavior — multi-batch iteration (>256 events), end-of-stream vs. mid-stream `EvtNext` failure, per-event `EvtRender` failure skipping, and handle release on every exit path (including an `on_xml` error partway through a batch) — be exercised by a scripted fake source without a live trace. Addresses MGudgin's review comment that `for_each_event_xml` had zero test coverage because every other test fed synthetic XML straight into `parse_events_from_xml`, bypassing the ETW walk entirely. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
`parse_event_xml` built a full roxmltree `Document` for every WinEvent record just to read a handful of fields (EventID, TimeCreated, Execution, and the EventData/Data|ComplexData texts). On a long access trace that is one DOM allocation + node graph per event. Replace it with a `quick-xml` streaming pull parser that walks the record once and allocates a `String` only for the leaf texts the decoders actually consume. Field semantics are preserved exactly, including roxmltree's "first matching element wins" `find(..)` behavior (via `seen_*` guards), the `unwrap_or` defaults, and the single hard requirement that a `<System>` element be present (otherwise `None`). roxmltree is now used only by a `#[test]` in `start.rs`, so it moves to `[dev-dependencies]` and no longer ships in the release binary. Addresses review comment r3582054960 on #585. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
The embedded-WPRP schema gate in start.rs was the last roxmltree consumer. Replace its DOM parse + descendant attribute lookups with a single quick-xml streaming pass: reading to EOF proves well-formedness, while Start/Empty events record the required <Profile Name= "AccessFailureProfile"> and <SystemCollector Id="SC_Kernel"> elements. With the per-event hot path already on quick-xml, roxmltree is now unused, so remove it from the plm dev-dependencies and the workspace dependency table (and the lockfile). The crate now depends on a single XML parser. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8737dded-f5f5-4794-90bb-5e666c9b85ca
- plm_launch: raise PLM_ELEVATED_WAIT_MS 30s -> 10min so wxc-exec doesn't kill the elevated `plm stop` child mid-parse (parse is advertised as "may take several minutes"); rewrite the doc comment to match. - access_failure: replace the duplicated test-only is_skippable with a thin wrapper over ParseAccumulator::is_skippable so tests exercise the real production filter instead of a parallel copy that can drift. - event_parser: compute is_drive_root from bytes (drop the unreachable unwrap_or(false)); spell out abbreviated locals in is_skippable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 299ac08c-3875-41fc-a31c-cc84bf5a545c
learning_mode, resource_type and app_path were carried over from the PowerShell port but are never read anywhere in the pipeline. Remove the struct fields, the learning_mode/resource_type parsing (and their now-unused EventData index constants), and the app_path store (the local is still used for the self-access filter). Updates the two config.rs test constructors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 299ac08c-3875-41fc-a31c-cc84bf5a545c
cwd_lc_trimmed retains a bare drive root (for the exact-equality match); only cwd_lc_prefix is None for a drive root. Fix the misplaced 'None for drive root' clause and document cwd_lc_prefix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
…action # Conflicts: # src/core/wxc/Cargo.toml
Contributor
Author
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
MGudgin
approved these changes
Jul 22, 2026
lilybarkley-msft
added a commit
that referenced
this pull request
Jul 23, 2026
Restacked onto main after PR #585 (PLM 2/6) was squash-merged. Collapses the pr3 review-fix commits into a single commit carrying pr3's net delta over main (config generation in src/host/plm). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
lilybarkley-msft
added a commit
that referenced
this pull request
Jul 23, 2026
Restacked onto pr3 after PR #585 squash-merge. Single commit carrying pr4's net delta over pr3. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
lilybarkley-msft
added a commit
that referenced
this pull request
Jul 23, 2026
Restacked onto pr4 after PR #585 squash-merge. Single commit carrying pr5's net delta over pr4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
lilybarkley-msft
added a commit
that referenced
this pull request
Jul 23, 2026
Restacked onto pr5 after PR #585 squash-merge. Single commit carrying pr6's net delta over pr5. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
lilybarkley-msft
added a commit
that referenced
this pull request
Jul 23, 2026
Restacked onto pr3 after PR #585 squash-merge. Single commit carrying pr4's net delta over pr3. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
lilybarkley-msft
added a commit
that referenced
this pull request
Jul 23, 2026
Restacked onto pr4 after PR #585 squash-merge. Single commit carrying pr5's net delta over pr4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
lilybarkley-msft
added a commit
that referenced
this pull request
Jul 23, 2026
Restacked onto pr5 after PR #585 squash-merge. Single commit carrying pr6's net delta over pr5. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd418a6e-8ffc-4432-b6c0-429739f504bb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📖 Description
PR 2 of 6 — stacked on PR1. Adds filesystem extraction to PLM.
EventID=14(access-failure) decoder:LearningModeAccessEventrows (file path + access mask)for_each_event_xml/EvtQuery/EvtRender) with bounded peak memoryParseAccumulator+ per-event dispatcher inevent_parserconfig.rsfoundations: load/parse,filesystem.{readwritePaths,readonlyPaths}merge,deny_file_setstop/logwired to the FS-extraction pipelineCapability extraction, config-generation summaries, and UI policy land in PR3–PR5.
requested_capabilitiesis exposed as an always-empty placeholder so call-sites stay stable across the split.🔗 References
user/lilybarkley/plm-pr1-audit-skeleton)Adjusted_<name>.jsonwrite + detection summaries)🔍 Validation
cargo build -p plm --target x86_64-pc-windows-msvc— cleancargo fmt --all -- --check— cleancargo clippy -p plm --target x86_64-pc-windows-msvc --all-targets -- -D warnings— cleancargo test -p plm --target x86_64-pc-windows-msvc— 37 passed (12 new over PR1; cover path-normalization, post-XPath filters, accumulator dispatch, ETW XML parsing).✅ Checklist
📋 Issue Type
GitHub Actions runs the PR validation build automatically. The ADO pipeline
(
MXC-PR-Build) is the official build pipeline that signs the binaries; itruns on merge to
mainand nightly, and Microsoft reviewers can trigger iton a PR with
/azp run. See docs/pull-requests.md.Microsoft Reviewers: Open in CodeFlow