Skip to content

fix(sccm): capture client logs beside CcmExec - #494

Merged
adamgell merged 4 commits into
mainfrom
codex/sccm-client-explicit-root-capture
Aug 5, 2026
Merged

fix(sccm): capture client logs beside CcmExec#494
adamgell merged 4 commits into
mainfrom
codex/sccm-client-explicit-root-capture

Conversation

@adamgell

@adamgell adamgell commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • derive exactly one private client log root beside a strictly validated CcmExec.exe service path
  • remove the fixed %WINDIR%\\CCM\\Logs fallback
  • reject UNC, relative, wrapper, traversal, ambiguous, ADS, repeated-separator, wrong-basename, and control-bearing service paths
  • reuse the existing exact catalog, rotation, cap, no-follow/reparse, hashing, opaque-provenance, and manifest pipeline
  • keep mtrmgr.log unsupported until observed evidence exists

Validation

  • discovery: 21 passed
  • native collection: 21 passed
  • client discovery: 25 passed
  • client manifest: 21 passed
  • parser spine: 161 passed
  • inventory/compliance/metering fixture contract: 47 passed
  • strict Clippy, changed-file rustfmt, and diff checks: green
  • independent review: initial REWORK on control validation after trim; fix at e33b436a independently ACCEPTED

Supports #483, #484, and #485. Those discovery issues remain open pending a fresh Windows capture and evidence audit.

Summary by CodeRabbit

  • New Features

    • SCCM log collection now derives the client log location from the exact CcmExec service configuration.
    • Added validation to reject invalid or unsafe service paths.
    • Added coverage for log selection, rotated logs, hashes, limits, and manifest integrity.
  • Bug Fixes

    • Removed reliance on a fixed default SCCM log directory.
    • Excludes unlisted logs such as mtrmgr.log from collection.
    • Prevents source paths from appearing in collected manifests.

@github-actions github-actions Bot added bug Something isn't working enhancement New feature or request parser Log parser related sccm SCCM/ConfigMgr related labels Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fa4d4eb7-8d99-499e-ae2b-bd8159958eb8

📥 Commits

Reviewing files that changed from the base of the PR and between e33b436 and 11fc0f7.

📒 Files selected for processing (1)
  • src-tauri/src/sccm/collector/discovery.rs

📝 Walkthrough

Walkthrough

The change derives the SCCM client log root from the exact CcmExec service path. It removes the fixed fallback, adds strict path validation, and expands native collection tests for filtering, absent sources, hashes, limits, and manifest privacy.

Changes

SCCM client capture

Layer / File(s) Summary
CcmExec service-root discovery
docs/superpowers/plans/..., src-tauri/src/sccm/collector/discovery.rs
CIM discovery returns structured service names and paths. The implementation validates the exact CcmExec.exe path and derives its sibling Logs directory. Tests cover valid, invalid, quoted, and private serialization cases.
Client capture integration
src-tauri/src/sccm/collector/discovery.rs, src-tauri/tests/sccm_native_collection.rs
The client role no longer uses the fixed %WINDIR%\CCM\Logs fallback. Tests cover absent sources, catalog filtering, rotations, mtrmgr.log exclusion, hashes, limits, and opaque manifest paths.
Validation and lab evidence
docs/superpowers/plans/...
The plan defines local checks, privacy checks, self-review checks, and authorized Windows-lab validation steps.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CIMQuery
  participant Discovery
  participant ClientCollector
  participant Manifest
  CIMQuery->>Discovery: Return CcmExec Name and PathName
  Discovery->>Discovery: Validate executable path
  Discovery->>ClientCollector: Provide derived Logs root
  ClientCollector->>Manifest: Record capture results without source paths
Loading

Possibly related PRs

Suggested labels: test

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits and accurately describes SCCM client log capture beside CcmExec.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/sccm-client-explicit-root-capture

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-08-04-sccm-client-service-root-capture.md`:
- Line 5: Replace the en dash between the issue references in the Goal line with
plain text such as “#483 through `#485`,” preserving the rest of the documentation
unchanged.
- Line 15: Update the “Task 1: Parse the exact CcmExec service path” heading to
level 2 so it follows the level-1 document title and satisfies the heading
hierarchy rule.

In `@src-tauri/src/sccm/collector/discovery.rs`:
- Around line 309-313: Update client_root_from_service_path to reject any
path_name whose original value differs from path_name.trim(), alongside the
existing control-character validation, then parse the original untrimmed value
rather than normalizing it. Add test cases covering leading and trailing
ordinary spaces next to the control-whitespace cases.

In `@src-tauri/tests/sccm_native_collection.rs`:
- Around line 352-353: Update the assertions in the artifact verification test
around artifact.content_sha256 to compare the digest value against the known
SHA-256 of content, rather than only checking that it is present. Preserve the
existing bytes_copied assertion and ensure the test fails for incorrect or
constant digests.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: aea083e5-5501-4752-b8e3-755d390795a7

📥 Commits

Reviewing files that changed from the base of the PR and between 8064b5a and e33b436.

📒 Files selected for processing (3)
  • docs/superpowers/plans/2026-08-04-sccm-client-service-root-capture.md
  • src-tauri/src/sccm/collector/discovery.rs
  • src-tauri/tests/sccm_native_collection.rs


> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Capture evidence for #483–#485 from the exact client log root beside a validated `CcmExec.exe`, without exposing paths or weakening catalog/cap/reparse controls.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace the en dash.

Line 5 uses an en dash between issue references. Replace it with text such as #483 through #485``.

As per path instructions, "**/*.md: Documentation. Verify commands, paths, and flags actually exist in the repo. Flag em dashes and en dashes."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-08-04-sccm-client-service-root-capture.md` at
line 5, Replace the en dash between the issue references in the Goal line with
plain text such as “#483 through `#485`,” preserving the rest of the documentation
unchanged.

Source: Path instructions


---

### Task 1: Parse the exact CcmExec service path

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the heading hierarchy.

Line 15 starts at heading level 3 after the level-1 document title. Change ### Task 1 to ## Task 1 to satisfy MD001.

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 15-15: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-08-04-sccm-client-service-root-capture.md` at
line 15, Update the “Task 1: Parse the exact CcmExec service path” heading to
level 2 so it follows the level-1 document title and satisfies the heading
hierarchy rule.

Source: Linters/SAST tools

Comment on lines +309 to +313
fn client_root_from_service_path(path_name: &str) -> Option<std::path::PathBuf> {
if path_name.chars().any(char::is_control) {
return None;
}
let path_name = path_name.trim();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject whitespace normalization in service paths.

Line 313 accepts leading or trailing ordinary spaces by trimming before validation. For example, a service value with a leading space becomes an accepted drive-qualified path. Reject values where path_name != path_name.trim() and parse the original value. Add plain-space cases beside the control-whitespace test.

Proposed fix
-    let path_name = path_name.trim();
-    if path_name.is_empty() {
+    if path_name.is_empty() || path_name != path_name.trim() {
         return None;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn client_root_from_service_path(path_name: &str) -> Option<std::path::PathBuf> {
if path_name.chars().any(char::is_control) {
return None;
}
let path_name = path_name.trim();
fn client_root_from_service_path(path_name: &str) -> Option<std::path::PathBuf> {
if path_name.chars().any(char::is_control) {
return None;
}
if path_name.is_empty() || path_name != path_name.trim() {
return None;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/sccm/collector/discovery.rs` around lines 309 - 313, Update
client_root_from_service_path to reject any path_name whose original value
differs from path_name.trim(), alongside the existing control-character
validation, then parse the original untrimmed value rather than normalizing it.
Add test cases covering leading and trailing ordinary spaces next to the
control-whitespace cases.

Comment on lines +352 to +353
assert_eq!(artifact.bytes_copied, content.len() as u64);
assert!(artifact.content_sha256.is_some());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Verify the digest value.

Line 353 only verifies that content_sha256 exists. A constant or incorrect digest passes this test. Compare it with the known SHA-256 of content.

As per path instructions, "src-tauri/tests/**/*.rs: Regression tests. Verify assertions test real behavior rather than restating the implementation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/tests/sccm_native_collection.rs` around lines 352 - 353, Update the
assertions in the artifact verification test around artifact.content_sha256 to
compare the digest value against the known SHA-256 of content, rather than only
checking that it is present. Preserve the existing bytes_copied assertion and
ensure the test fails for incorrect or constant digests.

Source: Path instructions

@adamgell

adamgell commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Final integration review ACCEPT at 11fc0f7. The delta is one cfg line: the byte-wrapper is test-only, while Windows runtime discovery still parses CIM output through apply_cim_service_facts and client_root_from_service_facts. Focused discovery tests and strict native Clippy pass. Hosted Windows release build is the remaining merge gate.

@adamgell
adamgell merged commit 1c5eac3 into main Aug 5, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request parser Log parser related sccm SCCM/ConfigMgr related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant