Skip to content

fix(sccm): harden client intake and CCM ambiguity (#319) - #435

Merged
adamgell merged 3 commits into
codex/parser-family-skeletonfrom
codex/sccm-319-client-hardening-clean-port-r1
Aug 2, 2026
Merged

fix(sccm): harden client intake and CCM ambiguity (#319)#435
adamgell merged 3 commits into
codex/parser-family-skeletonfrom
codex/sccm-319-client-hardening-clean-port-r1

Conversation

@adamgell

@adamgell adamgell commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Scope

Issue #319 pure hardening only:

  • makes MAX_SCCM_CLIENT_INTAKE_ARTIFACTS the authoritative 4,096-entry boundary for pure and native client intake;
  • rejects oversized direct bundles before validation indexes and oversized wire sequences while deserializing;
  • makes nested physical-line CCM openers coverage-only for SCCM evidence;
  • preserves the existing public LogEntry projection for LF/CRLF multiline messages;
  • keeps same-line literal opener text valid and bounds adversarial recovery to one newest-first scan.

The rejected normalized text-payload bridge is deliberately absent. No ingest.rs change, native I/O, Windows collection, Tauri command, workflow reducer, or live-lab claim is included.

Dependency state

Based on reviewed SCCM integration head 0b37e7a842991fd9cc9341f7602cf05186d03587. This small contract hardening must merge before the native #319 adapter is restacked so native capture imports the single authoritative limit.

TDD and adversarial matrix

  • direct 4,097-item input fails with the limit error before duplicate/identity validation;
  • wire 4,096 boundary succeeds and 4,097 fails during sequence deserialization;
  • LF and CRLF line-start opener ambiguity produces no SCCM evidence;
  • the same input remains one compatible public CCM LogEntry;
  • same-line literal opener remains one record;
  • 4,096 partial openers plus a final close remains coverage-only without restart rescanning.

Verification

  • cargo test --locked -p cmtraceopen-parser --test sccm_client_intake — 50 passed
  • focused CCM unit tests — 19 passed
  • cargo test --locked -p cmtraceopen-parser — passed
  • cargo clippy --locked -p cmtraceopen-parser --all-targets -- -D warnings — passed
  • rustup run 1.88.0 cargo check --locked -p cmtraceopen-parser --target wasm32-unknown-unknown — passed
  • changed-file Rust 1.88 rustfmt — passed
  • git diff --check — passed
  • independent exact-head review — GO
  • local CodeRabbit exact-range review — 0 findings

Repository-wide formatting still reports the documented unrelated baseline drift; all three changed files are clean.

Remaining #319 work

Native discovery/capture is still local and rejected on bounded enumeration, early reader cardinality, raw-path debug/error surfaces, direct-writer Windows ACL validation, and honest rollback failure reporting. Real Windows acceptance remains pending. Keep #319 open.

Summary by CodeRabbit

  • New Features

    • Added support for detecting CCM record openers and recovering from ambiguous nested entries.
    • Limited SCCM client intake bundles to 4,096 artifacts.
  • Bug Fixes

    • Improved handling of multiline, same-line, and nested opener patterns.
    • Oversized intake bundles are now rejected early with a clear limit-exceeded error.
  • Tests

    • Added coverage for newline variations, nested opener sequences, artifact-count boundaries, and oversized bundles.

Copilot AI review requested due to automatic review settings August 2, 2026 07:58
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ee26f67-1851-4f88-acf0-2734f1388933

📥 Commits

Reviewing files that changed from the base of the PR and between eb03235 and 074b412.

📒 Files selected for processing (3)
  • crates/cmtraceopen-parser/src/parser/ccm.rs
  • crates/cmtraceopen-parser/src/sccm/client/intake.rs
  • crates/cmtraceopen-parser/tests/sccm_client_intake.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/cmtraceopen-parser/tests/sccm_client_intake.rs
  • crates/cmtraceopen-parser/src/sccm/client/intake.rs
  • crates/cmtraceopen-parser/src/parser/ccm.rs

📝 Walkthrough

Walkthrough

The PR improves CCM nested-opener recovery and adds a 4096-artifact limit to SCCM client intake deserialization and validation. Regression tests cover parser ambiguity, boundary acceptance, and oversized input rejection.

Changes

CCM opener recovery

Layer / File(s) Summary
Nested opener scanning and regression coverage
crates/cmtraceopen-parser/src/parser/ccm.rs
The scanner detects nested physical-line openers, treats ambiguous SCCM evidence matches as plain text, preserves public projection behavior, and tests LF, CRLF, same-line, attribute-value, and adversarial inputs.

SCCM intake artifact limit

Layer / File(s) Summary
Bounded intake deserialization and validation
crates/cmtraceopen-parser/src/sccm/client/intake.rs, crates/cmtraceopen-parser/tests/sccm_client_intake.rs
Client intake bundles enforce the public 4096-artifact maximum during deserialization and validation. Tests cover oversized inputs, streaming JSON, and the exact boundary.

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

Possibly related issues

Possibly related PRs

Suggested labels: test

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the PR's two main changes: hardening SCCM client intake and handling CCM ambiguity.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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

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

@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 2, 2026
@adamgell

adamgell commented Aug 2, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens SCCM client intake bundle handling and CCM multiline opener ambiguity so SCCM evidence extraction fails closed under adversarial or malformed inputs, while preserving the existing public CCM LogEntry projection behavior.

Changes:

  • Introduces an authoritative MAX_SCCM_CLIENT_INTAKE_ARTIFACTS (4,096) limit and enforces it both during validation and during serde deserialization of intake bundles.
  • Updates the CCM multiline scanner to treat nested physical-line openers inside a complete-looking record as ambiguity (coverage-only) for SCCM evidence mode while keeping public parsing compatible.
  • Adds targeted tests covering bundle-size limits and CCM ambiguity cases for LF/CRLF and same-line literal openers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
crates/cmtraceopen-parser/tests/sccm_client_intake.rs Adds tests for bundle artifact count hard limits (validation + wire deserialization).
crates/cmtraceopen-parser/src/sccm/client/intake.rs Defines and enforces the 4,096 artifact ceiling, including bounded seq deserialization and early validation.
crates/cmtraceopen-parser/src/parser/ccm.rs Adds ambiguity detection for nested line-start CCM openers in SCCM evidence scanning and corresponding unit tests.

Comment thread crates/cmtraceopen-parser/src/parser/ccm.rs

@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.

🧹 Nitpick comments (2)
crates/cmtraceopen-parser/tests/sccm_client_intake.rs (1)

446-467: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a text-based JSON deserialization test to cover the loop fallback path.

This test builds inputs through serde_json::to_value and decodes them with serde_json::from_value. A Value-based deserializer knows the exact array length ahead of time, so the oversized case is rejected by the early size_hint() check in deserialize_bounded_client_artifacts (intake.rs Lines 237-244), never by the loop-plus-IgnoredAny fallback at intake.rs Lines 246-262.

Text-based JSON parsing (serde_json::from_str, from_slice, from_reader) does not provide a size hint before reading the array, so production code that deserializes bundles from raw JSON text relies on the loop fallback, not the early check. Add a test that serializes an oversized bundle to a JSON string and deserializes it with serde_json::from_str, to confirm the fallback path independently rejects an oversized sequence.

#[test]
fn intake_wire_rejects_more_than_the_v1_artifact_limit_from_json_text() {
    let artifact = synthetic_artifact("wire-limit-text", "PolicyAgent.log");
    let oversized = serde_json::json!({
        "artifacts": vec![artifact; MAX_SCCM_CLIENT_INTAKE_ARTIFACTS + 1],
    });
    let text = oversized.to_string();

    let error = serde_json::from_str::<SccmClientIntakeBundle>(&text)
        .expect_err("text-based JSON parsing must also reject an oversized artifact sequence");
    assert!(
        error.to_string().contains("artifact count exceeds"),
        "the streaming fallback must report the bounded-contract violation: {error}"
    );
}

Please confirm with a web search or the serde_json documentation that text-based deserializers (from_str/from_slice/from_reader) do not provide SeqAccess::size_hint, while from_value does, since this determines which branch of deserialize_bounded_client_artifacts each caller actually exercises.

🤖 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 `@crates/cmtraceopen-parser/tests/sccm_client_intake.rs` around lines 446 -
467, Add a separate test near
intake_wire_rejects_more_than_the_v1_artifact_limit_while_deserializing that
builds an oversized bundle, serializes it to JSON text, and deserializes it with
serde_json::from_str; assert deserialization fails and the error contains
“artifact count exceeds,” thereby exercising the loop fallback in
deserialize_bounded_client_artifacts rather than the size_hint path.
crates/cmtraceopen-parser/src/sccm/client/intake.rs (1)

233-266: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse SccmClientIntakeError::ArtifactLimitExceeded instead of duplicating its message text.

Lines 241-243 and 259-261 hardcode "client intake artifact count exceeds the supported limit" as a separate literal from the SccmClientIntakeError::ArtifactLimitExceeded variant defined at Line 582. Three copies of the same message text now exist. If one copy changes, the wire deserializer and the native validate_bundle path can silently report inconsistent text. Pass the error enum directly, since A::Error::custom accepts any Display and thiserror already implements Display for SccmClientIntakeError::ArtifactLimitExceeded.

♻️ Proposed fix to remove the duplicated literal
             if sequence
                 .size_hint()
                 .is_some_and(|size| size > MAX_SCCM_CLIENT_INTAKE_ARTIFACTS)
             {
-                return Err(A::Error::custom(
-                    "client intake artifact count exceeds the supported limit",
-                ));
+                return Err(A::Error::custom(
+                    crate::sccm::client::intake::SccmClientIntakeError::ArtifactLimitExceeded,
+                ));
             }
@@
             if sequence.next_element::<IgnoredAny>()?.is_some() {
-                return Err(A::Error::custom(
-                    "client intake artifact count exceeds the supported limit",
-                ));
+                return Err(A::Error::custom(
+                    crate::sccm::client::intake::SccmClientIntakeError::ArtifactLimitExceeded,
+                ));
             }
🤖 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 `@crates/cmtraceopen-parser/src/sccm/client/intake.rs` around lines 233 - 266,
Update both limit-exceeded branches in the `visit_seq` implementation to pass
`SccmClientIntakeError::ArtifactLimitExceeded` directly to `A::Error::custom`
instead of duplicating the message literal, preserving the existing
deserialization behavior and shared error text.
🤖 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.

Nitpick comments:
In `@crates/cmtraceopen-parser/src/sccm/client/intake.rs`:
- Around line 233-266: Update both limit-exceeded branches in the `visit_seq`
implementation to pass `SccmClientIntakeError::ArtifactLimitExceeded` directly
to `A::Error::custom` instead of duplicating the message literal, preserving the
existing deserialization behavior and shared error text.

In `@crates/cmtraceopen-parser/tests/sccm_client_intake.rs`:
- Around line 446-467: Add a separate test near
intake_wire_rejects_more_than_the_v1_artifact_limit_while_deserializing that
builds an oversized bundle, serializes it to JSON text, and deserializes it with
serde_json::from_str; assert deserialization fails and the error contains
“artifact count exceeds,” thereby exercising the loop fallback in
deserialize_bounded_client_artifacts rather than the size_hint path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5af3c8f6-1e14-44eb-898f-913cab568be5

📥 Commits

Reviewing files that changed from the base of the PR and between 0b37e7a and eb03235.

📒 Files selected for processing (3)
  • crates/cmtraceopen-parser/src/parser/ccm.rs
  • crates/cmtraceopen-parser/src/sccm/client/intake.rs
  • crates/cmtraceopen-parser/tests/sccm_client_intake.rs

@adamgell

adamgell commented Aug 2, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@adamgell
adamgell merged commit ce774fd into codex/parser-family-skeleton Aug 2, 2026
11 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 test Testing related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants