feat(intune): implement Windows device compliance analyzer (#364) - #495
Conversation
Fill the reserved compliance leaf with a pure four-phase reducer (local evaluation → aggregate → reporting → access), full fixture matrix, and deterministic privacy redaction. Ports the audited module from PR #449 onto current main so CI is not blocked by the stale company-portal wasm purity failure on the old base.
📝 WalkthroughWalkthroughChangesWindows Intune compliance
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant EvidenceSources
participant BundleDecoder
participant ComplianceReducer
participant FindingRules
participant ComplianceSnapshot
EvidenceSources->>BundleDecoder: provide versioned evidence
BundleDecoder->>ComplianceReducer: return normalized ComplianceInput
ComplianceReducer->>FindingRules: provide phased snapshot state
FindingRules->>ComplianceSnapshot: attach derived findings
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Biome (2.5.6)crates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/evidence/mdm-events/current/mdm-events.jsonFile contains syntax errors that prevent linting: Line 5: expected Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 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 `@crates/cmtraceopen-parser/src/intune/device/windows/compliance/models.rs`:
- Around line 36-56: Document each public variant of ComplianceScope and each
public field of ComplianceSettingKey with concise /// comments describing its
meaning, while preserving the existing derives, serde attributes, and types.
In `@crates/cmtraceopen-parser/src/intune/device/windows/compliance/redaction.rs`:
- Around line 44-46: Update sid_re to use case-insensitive matching and allow
the variable numeric-component forms required for short-form, domain, and
lowercase SIDs, while preserving full SID matching. Add tests covering S-1-5-18,
S-1-5-32-544, and lowercase domain SIDs through redact_text.
- Around line 25-31: Replace the unkeyed hashing in stable_token with
HMAC-SHA-256 keyed by a deployment-provided secret, and update the public
projection API and all callers to receive and propagate that secret. Keep the
secret independent of the identifier value and never serialize or derive it from
public input; preserve the stable token format using an appropriate
deterministic digest representation.
In `@crates/cmtraceopen-parser/src/intune/device/windows/compliance/reducer.rs`:
- Around line 100-103: Update analyze_compliance to classify the event set once,
then pass the resulting signals into both reduce_local and reduce_reporting.
Remove the duplicate classify_event traversal from reduce_reporting while
preserving its filtering to ReportSubmission signals, and adjust reduce_local to
consume the shared classifications so both phases use identical results.
- Around line 560-571: Update the freshness handling around the generated_at
check so a future-stamped service result is downgraded only on that result
rather than setting phase.service_freshness to Unknown. Recompute the aggregate
phase freshness after modifying the offending result, preserving stale freshness
and findings for unaffected records; use the existing result freshness field and
fold logic.
- Around line 505-507: Update the submission handling in the compliance
reduction loop to retain the newest normalized submitted_at timestamp rather
than overwriting it based on input order. Ensure report_id and
last_submission_at use consistent newest-by-timestamp semantics, and add
coverage with newest-first Submitted events asserting the final state remains
Submitted.
- Around line 132-146: Update the policy-state handling in the
ComplianceSignal::PolicyState arm to apply the same explicit ranking rule as the
neighbouring prerequisite arm: retain a definite state over Unknown while
preserving the existing observation otherwise. Add a comment documenting this
first-wins/ranking behavior, and ensure later conflicting states are not
silently treated as equivalent.
In `@crates/cmtraceopen-parser/src/intune/device/windows/compliance/rules.rs`:
- Around line 337-341: Update ComplianceReportingPhase to expose a dedicated
submission_evidence field, populate it only from report-submission event
references in reduce_reporting, and use it instead of reporting.evidence in the
submission-failure finding. Preserve service-record evidence in the general
reporting evidence collection and add the public field before publication.
In `@crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs`:
- Around line 528-530: The EvaluatedAt fallback accepts invalid synthetic
timestamps, preventing valid source timestamps from being used. In
crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs:528-530
within classify_event and :588-590 within classify_setting_report, filter
synthetic_timestamp results to those with normalized_utc present before calling
or_else, preserving fallback to the respective context source_timestamp; add a
test with a non-RFC-3339 EvaluatedAt and valid source_timestamp that asserts
evaluated_at is orderable.
- Around line 290-302: Update parse_setting_result to handle numeric verdict
tokens consistently: remove "0" from the Compliant mappings unless the parser
explicitly supports the complete numeric vocabulary. Preserve the existing
textual mappings and return None for unsupported numeric values such as "0" and
"1".
- Around line 387-394: Update the decimal handling in parse_error_code so hex
formatting preserves the full parsed i64 value instead of casting through u32.
Keep raw and decimal unchanged, and ensure values beyond 32-bit range render
without truncation, matching the existing hex-branch behavior.
In
`@crates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/manifest.json`:
- Line 29: Update both captureState entries in the malformed-and-unknown-schema
fixture manifest to "captured", preserving the fixture’s malformed JSON and
version 99 inputs so sources() and decode_bundle exercise their intended
classification paths.
In `@crates/cmtraceopen-parser/tests/intune_windows_compliance.rs`:
- Around line 579-602: Expand redaction_preserves_states_coverage_and_evidence
to compare every non-PII ComplianceSnapshot field between projected and
loaded.snapshot, including reporting, local setting state and evidence, access
decisions and evidence, and timestamps. Preserve the existing aggregate,
coverage, and finding evidence assertions, while excluding only fields
intentionally redacted.
🪄 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: 7836b1b3-39dd-4d44-aa3e-220999779a1b
📒 Files selected for processing (80)
crates/cmtraceopen-parser/src/intune/device/windows/compliance/mod.rscrates/cmtraceopen-parser/src/intune/device/windows/compliance/models.rscrates/cmtraceopen-parser/src/intune/device/windows/compliance/redaction.rscrates/cmtraceopen-parser/src/intune/device/windows/compliance/reducer.rscrates/cmtraceopen-parser/src/intune/device/windows/compliance/rules.rscrates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rscrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-with-matching-state/evidence/access-facts/current/access-facts.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-with-matching-state/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-with-matching-state/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-with-matching-state/evidence/service-facts/current/service-facts.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-with-matching-state/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-with-matching-state/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-without-compliance-evidence/evidence/access-facts/current/access-facts.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-without-compliance-evidence/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-without-compliance-evidence/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/access-denied-without-compliance-evidence/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/contradictory-ids-and-timestamps/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/contradictory-ids-and-timestamps/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/contradictory-ids-and-timestamps/evidence/mdm-report/current/mdm-report.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/contradictory-ids-and-timestamps/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/contradictory-ids-and-timestamps/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-discovery-noncompliant/evidence/custom-compliance/current/custom-compliance.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-discovery-noncompliant/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-discovery-noncompliant/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-discovery-noncompliant/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-script-failure/evidence/custom-compliance/current/custom-compliance.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-script-failure/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-script-failure/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/custom-compliance-script-failure/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/deterministic-privacy-redaction/evidence/access-facts/current/access-facts.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/deterministic-privacy-redaction/evidence/custom-compliance/current/custom-compliance.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/deterministic-privacy-redaction/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/deterministic-privacy-redaction/evidence/mdm-report/current/mdm-report.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/deterministic-privacy-redaction/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/deterministic-privacy-redaction/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/evaluation-error/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/evaluation-error/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/evaluation-error/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/evaluation-error/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-service-compliant/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-service-compliant/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-service-compliant/evidence/service-facts/current/service-facts.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-service-compliant/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-service-compliant/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-stale-service-noncompliant/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-stale-service-noncompliant/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-stale-service-noncompliant/evidence/service-facts/current/service-facts.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-stale-service-noncompliant/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-compliant-stale-service-noncompliant/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-setting/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-setting/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-setting/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-setting/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-with-later-service-update/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-with-later-service-update/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-with-later-service-update/evidence/service-facts/current/service-facts.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-with-later-service-update/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/local-noncompliant-with-later-service-update/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/evidence/mdm-report/current/mdm-report.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/partial-event-and-report-coverage/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/partial-event-and-report-coverage/evidence/mdm-report/current/mdm-report.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/partial-event-and-report-coverage/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/partial-event-and-report-coverage/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/report-submission-failure/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/report-submission-failure/evidence/mdm-events/current/mdm-events.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/report-submission-failure/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/report-submission-failure/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/unsupported-not-applicable-setting/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/unsupported-not-applicable-setting/evidence/mdm-report/current/mdm-report.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/unsupported-not-applicable-setting/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/unsupported-not-applicable-setting/manifest.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/user-targeted-policy-not-evaluated/evidence/device-context/current/device-context.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/user-targeted-policy-not-evaluated/evidence/mdm-report/current/mdm-report.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/user-targeted-policy-not-evaluated/expected.jsoncrates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/user-targeted-policy-not-evaluated/manifest.jsoncrates/cmtraceopen-parser/tests/intune_windows_compliance.rs
| #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub enum ComplianceScope { | ||
| Device, | ||
| User, | ||
| Unknown, | ||
| } | ||
|
|
||
| /// The identifiers a compliance setting can be keyed on. | ||
| /// | ||
| /// All three are optional because different sources supply different subsets: | ||
| /// an MDM event usually carries an OMA-DM node URI, an exported report row | ||
| /// usually carries policy and setting GUIDs. Observations are only merged when | ||
| /// they share a derived grouping token; see [`ComplianceSettingEvaluation::grouping_token`]. | ||
| #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub struct ComplianceSettingKey { | ||
| pub policy_id: Option<String>, | ||
| pub setting_id: Option<String>, | ||
| pub setting_uri: Option<String>, | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check whether the parser crate enforces missing_docs.
set -euo pipefail
fd -t f 'lib.rs' crates/cmtraceopen-parser/src --exec rg -n 'missing_docs|deny|warn' {}
fd -t f 'Cargo.toml' crates/cmtraceopen-parser --exec rg -n -A 15 '\[lints' {}
rg -n 'missing_docs|RUSTFLAGS' --iglob '*.toml' --iglob '*.yml' --iglob '*.yaml' .Repository: adamgell/cmtraceopen
Length of output: 158
🏁 Script executed:
set -euo pipefail
printf '%s\n' '--- parser crate files ---'
git ls-files crates/cmtraceopen-parser | sed -n '1,120p'
printf '%s\n' '--- lint configuration ---'
rg -n -C 3 'missing_docs|RUSTFLAGS|\[lints\]|rustdoc|warnings' \
--glob 'Cargo.toml' --glob '*.rs' --glob '*.yml' --glob '*.yaml' .
printf '%s\n' '--- crate root and target module ---'
for f in $(git ls-files crates/cmtraceopen-parser/src | grep -E '(^|/)(lib|models)\.rs$|cmtraceopen-parser/src/intune/device/windows/compliance/models\.rs$'); do
echo "### $f"
sed -n '1,180p' "$f"
doneRepository: adamgell/cmtraceopen
Length of output: 50376
🏁 Script executed:
set -euo pipefail
printf '%s\n' '--- parser manifest ---'
cat -n crates/cmtraceopen-parser/Cargo.toml
printf '%s\n' '--- parser crate root ---'
cat -n crates/cmtraceopen-parser/src/lib.rs | sed -n '1,220p'
printf '%s\n' '--- workspace lint sections ---'
rg -n -C 8 '^\[workspace\.lints|^\[lints|missing_docs|RUSTFLAGS|rustdocflags|D warnings' \
Cargo.toml crates/cmtraceopen-parser/Cargo.toml .github .cargo 2>/dev/null || true
printf '%s\n' '--- target public-item documentation ---'
python3 - <<'PY'
from pathlib import Path
p = Path("crates/cmtraceopen-parser/src/intune/device/windows/compliance/models.rs")
lines = p.read_text().splitlines()
for i, line in enumerate(lines, 1):
if 30 <= i <= 70:
print(f"{i:4}: {line}")
PY
printf '%s\n' '--- change status and focused diff summary ---'
git status --short
git diff --stat -- crates/cmtraceopen-parser/src/intune/device/windows/compliance/models.rs
git diff --unified=3 -- crates/cmtraceopen-parser/src/intune/device/windows/compliance/models.rs | sed -n '1,220p'Repository: adamgell/cmtraceopen
Length of output: 8764
Document the public variants and fields.
missing_docs is not enabled in this crate or CI. However, the path instructions require documentation for new public items. Add /// comments to the enum variants and struct fields in this module.
🤖 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/intune/device/windows/compliance/models.rs`
around lines 36 - 56, Document each public variant of ComplianceScope and each
public field of ComplianceSettingKey with concise /// comments describing its
meaning, while preserving the existing derives, serde attributes, and types.
Source: Path instructions
| fn stable_token(kind: &str, value: &str) -> String { | ||
| let mut hash: u64 = 0xcbf2_9ce4_8422_2325; | ||
| for byte in value.as_bytes() { | ||
| hash ^= u64::from(*byte); | ||
| hash = hash.wrapping_mul(0x0000_0100_0000_01b3); | ||
| } | ||
| format!("[{kind}:{hash:016x}]") |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -e
file="crates/cmtraceopen-parser/src/intune/device/windows/compliance/redaction.rs"
printf '%s\n' '--- redaction.rs outline ---'
ast-grep outline "$file" || true
printf '%s\n' '--- redaction.rs ---'
cat -n "$file"
printf '%s\n' '--- redaction and hashing references ---'
rg -n --glob '*.rs' 'stable_token|redacted_export_projection|redact_text|Hmac|hmac|sha2|blake3|secret|key' crates src-tauri
printf '%s\n' '--- dependency manifests ---'
git ls-files '*Cargo.toml' | xargs -r rg -n 'hmac|sha2|blake3|ring|rust-crypto|secrecy'Repository: adamgell/cmtraceopen
Length of output: 50377
🏁 Script executed:
#!/bin/bash
set -e
file="crates/cmtraceopen-parser/src/intune/device/windows/compliance/redaction.rs"
printf '%s\n' '--- redaction implementation ---'
cat -n "$file"
printf '%s\n' '--- direct callers and exports ---'
rg -n -B3 -A5 --glob '*.rs' 'redacted_export_projection|redact_text|stable_token' crates/cmtraceopen-parser/src src-tauri/src | head -n 300
printf '%s\n' '--- manifests and crypto dependencies ---'
for f in $(git ls-files '*Cargo.toml'); do
if rg -q 'hmac|sha2|blake3|ring|rust-crypto|secrecy' "$f"; then
printf '%s\n' "--- $f ---"
rg -n 'hmac|sha2|blake3|ring|rust-crypto|secrecy' "$f"
fi
done
printf '%s\n' '--- public API declarations ---'
rg -n -B3 -A8 'pub (fn|struct|enum|type|mod)|pub use' "$file" crates/cmtraceopen-parser/src/intune/device/windows/compliance/mod.rsRepository: adamgell/cmtraceopen
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- compliance module exports ---'
sed -n '100,145p' crates/cmtraceopen-parser/src/intune/device/windows/compliance/mod.rs
printf '%s\n' '--- compliance projection callers ---'
rg -n -B4 -A8 --glob '*.rs' 'compliance::redacted_export_projection|redacted_export_projection\([^)]*snapshot|redacted_export_projection\(' crates/cmtraceopen-parser/src src-tauri/src | rg -C3 'compliance|snapshot|redacted_export_projection' | head -n 240
printf '%s\n' '--- crate public re-exports ---'
rg -n -B3 -A4 'pub use.*compliance|pub mod compliance|pub use redaction' crates/cmtraceopen-parser/src
printf '%s\n' '--- FNV-1a candidate probe ---'
python3 - <<'PY'
def fnv1a64(value):
h = 0xcbf29ce484222325
for b in value.encode():
h ^= b
h = (h * 0x100000001b3) & ((1 << 64) - 1)
return f"{h:016x}"
for value in [
"alice@example.com",
"bob@example.com",
"device-123456",
"tenant-abcdef",
]:
print(f"{value} -> {fnv1a64(value)}")
PYRepository: adamgell/cmtraceopen
Length of output: 22544
Weak Cryptography (CWE-328): Use of Weak Hash
Reachability: External · Exploitability: Trivial
Reachability path
● Entry
crates/cmtraceopen-parser/src/intune/device/windows/compliance/mod.rs:107
redaction
│
▼
● Sink
crates/cmtraceopen-parser/src/intune/device/windows/compliance/redaction.rs
Replace FNV-1a with a keyed cryptographic pseudonym.
stable_token produces unkeyed 64-bit digests for exported identifiers. Recipients can enumerate candidate values and match identities offline. Update the public projection API to receive a deployment-provided secret and use HMAC-SHA-256. Never serialize or derive the secret from public input.
🤖 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/intune/device/windows/compliance/redaction.rs`
around lines 25 - 31, Replace the unkeyed hashing in stable_token with
HMAC-SHA-256 keyed by a deployment-provided secret, and update the public
projection API and all callers to receive and propagate that secret. Keep the
secret independent of the identifier value and never serialize or derive it from
public input; preserve the stable token format using an appropriate
deterministic digest representation.
| fn sid_re() -> &'static Regex { | ||
| static CELL: OnceLock<Regex> = OnceLock::new(); | ||
| CELL.get_or_init(|| Regex::new(r"S-1-(?:\d+-){3,}\d+").expect("SID regex must compile")) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '1,220p' crates/cmtraceopen-parser/src/intune/device/windows/compliance/redaction.rs
printf '\n-- redaction references --\n'
rg -n "redact_text|sid_re|S-1-|redaction" crates/cmtraceopen-parser/src crates/cmtraceopen-parser/testsRepository: adamgell/cmtraceopen
Length of output: 50376
🏁 Script executed:
python3 - <<'PY'
import re
pattern = re.compile(r"S-1-(?:\d+-){3,}\d+")
cases = [
"S-1-5-18",
"S-1-5-32-544",
"S-1-5-21-111-222-333-1001",
"s-1-5-21-111-222-333-1001",
]
for value in cases:
print(f"{value}: {bool(pattern.search(value))}")
PYRepository: adamgell/cmtraceopen
Length of output: 259
Sensitive Data Exposure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor
Reachability: External
Reachability path
● Entry
crates/cmtraceopen-parser/src/intune/device/windows/compliance/mod.rs:107
redaction
│
▼
● Sink
crates/cmtraceopen-parser/src/intune/device/windows/compliance/redaction.rs
Mask short-form and lowercase SIDs.
sid_re misses valid SIDs such as S-1-5-18, S-1-5-32-544, and lowercase domain SIDs. These values bypass redact_text and can reach the default export. Use a case-insensitive pattern with a variable number of numeric components, and add tests for these forms.
🤖 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/intune/device/windows/compliance/redaction.rs`
around lines 44 - 46, Update sid_re to use case-insensitive matching and allow
the variable numeric-component forms required for short-form, domain, and
lowercase SIDs, while preserving full SID matching. Add tests covering S-1-5-18,
S-1-5-32-544, and lowercase domain SIDs through redact_text.
| for event in &input.events { | ||
| evidence.push(event.context.evidence_ref.clone()); | ||
| let reference = event.context.evidence_ref.clone(); | ||
| match classify_event(event) { |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
classify_event runs twice over every event.
reduce_local classifies the full event set on line 103, then reduce_reporting classifies the same set again on line 478 and discards everything that is not a ReportSubmission.
classify_event is not cheap. Each setting observation clones named_data and the whole IntuneObservationContext (sources.rs lines 531-532). For an EVTX-derived bundle this doubles both the classification work and the allocation volume, and the second pass throws away almost all of it.
Classify once in analyze_compliance and pass the resulting signals into both phases. That also removes the risk of the two call sites drifting apart if the classification rules change.
Also applies to: 472-481
🤖 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/intune/device/windows/compliance/reducer.rs`
around lines 100 - 103, Update analyze_compliance to classify the event set
once, then pass the resulting signals into both reduce_local and
reduce_reporting. Remove the duplicate classify_event traversal from
reduce_reporting while preserving its filtering to ReportSubmission signals, and
adjust reduce_local to consume the shared classifications so both phases use
identical results.
| Some(ComplianceSignal::PolicyState { | ||
| policy_id, | ||
| state, | ||
| scope, | ||
| }) => { | ||
| let entry = policies.entry(policy_id.clone()).or_insert_with(|| { | ||
| CompliancePolicyObservation { | ||
| policy_id, | ||
| state, | ||
| scope, | ||
| evidence: Vec::new(), | ||
| } | ||
| }); | ||
| entry.evidence.push(reference); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Policy state resolves first-wins with no contradiction signal.
or_insert_with records state and scope from the first observation only. A later observation that reports NotReceived after Received is dropped without a trace.
The neighbouring prerequisite arm on lines 115-130 handles the same situation explicitly and documents the ranking. merge_setting surfaces disagreement through identity_contradiction rather than resolving it. CompliancePolicyObservation is public output, so this branch is the only place in the module that silently picks a side.
At minimum, apply the same explicit rule the prerequisite arm uses, and state it in a comment.
♻️ Proposed change: rank a definite state over `Unknown`
let entry = policies.entry(policy_id.clone()).or_insert_with(|| {
CompliancePolicyObservation {
policy_id,
state,
scope,
evidence: Vec::new(),
}
});
+ // A definite claim outranks `Unknown`; two disagreeing definite
+ // claims are left at the first, matching the merge policy for
+ // settings rather than preferring the newer source.
+ if entry.state == CompliancePolicyState::Unknown {
+ entry.state = state;
+ }
+ if entry.scope == ComplianceScope::Unknown {
+ entry.scope = scope;
+ }
entry.evidence.push(reference);📝 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.
| Some(ComplianceSignal::PolicyState { | |
| policy_id, | |
| state, | |
| scope, | |
| }) => { | |
| let entry = policies.entry(policy_id.clone()).or_insert_with(|| { | |
| CompliancePolicyObservation { | |
| policy_id, | |
| state, | |
| scope, | |
| evidence: Vec::new(), | |
| } | |
| }); | |
| entry.evidence.push(reference); | |
| } | |
| Some(ComplianceSignal::PolicyState { | |
| policy_id, | |
| state, | |
| scope, | |
| }) => { | |
| let entry = policies.entry(policy_id.clone()).or_insert_with(|| { | |
| CompliancePolicyObservation { | |
| policy_id, | |
| state, | |
| scope, | |
| evidence: Vec::new(), | |
| } | |
| }); | |
| // A definite claim outranks `Unknown`; two disagreeing definite | |
| // claims are left at the first, matching the merge policy for | |
| // settings rather than preferring the newer source. | |
| if entry.state == CompliancePolicyState::Unknown { | |
| entry.state = state; | |
| } | |
| if entry.scope == ComplianceScope::Unknown { | |
| entry.scope = scope; | |
| } | |
| entry.evidence.push(reference); | |
| } |
🤖 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/intune/device/windows/compliance/reducer.rs`
around lines 132 - 146, Update the policy-state handling in the
ComplianceSignal::PolicyState arm to apply the same explicit ranking rule as the
neighbouring prerequisite arm: retain a definite state over Unknown while
preserving the existing observation otherwise. Add a comment documenting this
first-wins/ranking behavior, and ensure later conflicting states are not
silently treated as equivalent.
| pub(super) fn parse_setting_result(raw: &str) -> Option<ComplianceSettingState> { | ||
| match canonical(raw).as_str() { | ||
| "compliant" | "pass" | "passed" | "true" | "0" => Some(ComplianceSettingState::Compliant), | ||
| "noncompliant" | "notcompliant" | "fail" | "failed" | "false" => { | ||
| Some(ComplianceSettingState::Noncompliant) | ||
| } | ||
| "error" | "evaluationerror" => Some(ComplianceSettingState::EvaluationError), | ||
| "notapplicable" | "na" | "unsupported" => Some(ComplianceSettingState::NotApplicable), | ||
| "notevaluated" | "skipped" | "pending" => Some(ComplianceSettingState::NotEvaluated), | ||
| "prerequisiteunmet" => Some(ComplianceSettingState::PrerequisiteUnmet), | ||
| _ => None, | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
"0" asserts compliance while every other numeric token asserts nothing.
Line 292 maps the token 0 to Compliant. No arm maps 1 or any other numeric token. canonical("1") is "1", which falls through to None. A source that encodes its verdict as an HRESULT or as a 0/1 integer therefore produces an affirmative Compliant for success and no verdict at all for failure. The failure then degrades to InsufficientEvidence, or to EvaluationError only if the event level happens to be Error.
The asymmetry is the risk. 0 is the only token in this table that makes a positive claim from a numeric vocabulary the module has not otherwise declared. Either drop the 0 arm so numeric verdicts are consistently declined, or add the matching nonzero handling.
♻️ Option: decline numeric verdicts consistently
- "compliant" | "pass" | "passed" | "true" | "0" => Some(ComplianceSettingState::Compliant),
+ "compliant" | "pass" | "passed" | "true" => Some(ComplianceSettingState::Compliant),📝 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.
| pub(super) fn parse_setting_result(raw: &str) -> Option<ComplianceSettingState> { | |
| match canonical(raw).as_str() { | |
| "compliant" | "pass" | "passed" | "true" | "0" => Some(ComplianceSettingState::Compliant), | |
| "noncompliant" | "notcompliant" | "fail" | "failed" | "false" => { | |
| Some(ComplianceSettingState::Noncompliant) | |
| } | |
| "error" | "evaluationerror" => Some(ComplianceSettingState::EvaluationError), | |
| "notapplicable" | "na" | "unsupported" => Some(ComplianceSettingState::NotApplicable), | |
| "notevaluated" | "skipped" | "pending" => Some(ComplianceSettingState::NotEvaluated), | |
| "prerequisiteunmet" => Some(ComplianceSettingState::PrerequisiteUnmet), | |
| _ => None, | |
| } | |
| } | |
| pub(super) fn parse_setting_result(raw: &str) -> Option<ComplianceSettingState> { | |
| match canonical(raw).as_str() { | |
| "compliant" | "pass" | "passed" | "true" => Some(ComplianceSettingState::Compliant), | |
| "noncompliant" | "notcompliant" | "fail" | "failed" | "false" => { | |
| Some(ComplianceSettingState::Noncompliant) | |
| } | |
| "error" | "evaluationerror" => Some(ComplianceSettingState::EvaluationError), | |
| "notapplicable" | "na" | "unsupported" => Some(ComplianceSettingState::NotApplicable), | |
| "notevaluated" | "skipped" | "pending" => Some(ComplianceSettingState::NotEvaluated), | |
| "prerequisiteunmet" => Some(ComplianceSettingState::PrerequisiteUnmet), | |
| _ => 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 `@crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs`
around lines 290 - 302, Update parse_setting_result to handle numeric verdict
tokens consistently: remove "0" from the Compliant mappings unless the parser
explicitly supports the complete numeric vocabulary. Preserve the existing
textual mappings and return None for unsupported numeric values such as "0" and
"1".
| if decimal_code_re().is_match(raw) { | ||
| let decimal = raw.parse::<i64>().ok(); | ||
| return Some(IntuneErrorCode { | ||
| raw: raw.to_owned(), | ||
| decimal, | ||
| hex: decimal.map(|value| format!("0x{:08X}", value as u32)), | ||
| }); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
The decimal branch truncates to 32 bits, which the hex branch explicitly forbids.
Line 392 renders value as u32. The comment on lines 371-373 states that a 64-bit token must not silently become a 32-bit one, and the hex branch honors that. The decimal branch does not. parse_error_code("4294967297") yields hex: Some("0x00000001"), so the derived hex contradicts the preserved raw. The test on line 766 covers only the hex branch.
🐛 Proposed fix: widen the rendering to match the value
if decimal_code_re().is_match(raw) {
let decimal = raw.parse::<i64>().ok();
return Some(IntuneErrorCode {
raw: raw.to_owned(),
decimal,
- hex: decimal.map(|value| format!("0x{:08X}", value as u32)),
+ hex: decimal.map(|value| {
+ let bits = value as u64;
+ if bits <= u64::from(u32::MAX) {
+ format!("0x{bits:08X}")
+ } else {
+ format!("0x{bits:016X}")
+ }
+ }),
});
}📝 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.
| if decimal_code_re().is_match(raw) { | |
| let decimal = raw.parse::<i64>().ok(); | |
| return Some(IntuneErrorCode { | |
| raw: raw.to_owned(), | |
| decimal, | |
| hex: decimal.map(|value| format!("0x{:08X}", value as u32)), | |
| }); | |
| } | |
| if decimal_code_re().is_match(raw) { | |
| let decimal = raw.parse::<i64>().ok(); | |
| return Some(IntuneErrorCode { | |
| raw: raw.to_owned(), | |
| decimal, | |
| hex: decimal.map(|value| { | |
| let bits = value as u64; | |
| if bits <= u64::from(u32::MAX) { | |
| format!("0x{bits:08X}") | |
| } else { | |
| format!("0x{bits:016X}") | |
| } | |
| }), | |
| }); | |
| } |
🤖 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/intune/device/windows/compliance/sources.rs`
around lines 387 - 394, Update the decimal handling in parse_error_code so hex
formatting preserves the full parsed i64 value instead of casting through u32.
Keep raw and decimal unchanged, and ensure values beyond 32-bit range render
without truncation, matching the existing hex-branch behavior.
| evaluated_at: named_any(data, &EVALUATED_AT_NAMES) | ||
| .map(synthetic_timestamp) | ||
| .or_else(|| event.context.source_timestamp.clone()), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
An unparseable EvaluatedAt value discards a usable source_timestamp in both classifiers. synthetic_timestamp returns Some with kind: Invalid and normalized_utc: None when parsing fails on line 621, so the .or_else fallback never runs. normalized_timestamp in reducer.rs line 721 then rejects the timestamp, latest_evaluation skips the setting, and service freshness degrades to Unknown across the snapshot.
crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs#L528-L530: inclassify_event, insert.filter(|stamp| stamp.normalized_utc.is_some())between.map(synthetic_timestamp)and.or_else(...)so a failed parse falls back toevent.context.source_timestamp.crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs#L588-L590: apply the identical.filter(...)inclassify_setting_reportso a failed parse falls back toreport.context.source_timestamp.
Add one test that supplies a non-RFC-3339 EvaluatedAt alongside a valid source_timestamp and asserts the resulting evaluated_at is orderable.
📍 Affects 1 file
crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs#L528-L530(this comment)crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs#L588-L590
🤖 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/intune/device/windows/compliance/sources.rs`
around lines 528 - 530, The EvaluatedAt fallback accepts invalid synthetic
timestamps, preventing valid source timestamps from being used. In
crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs:528-530
within classify_event and :588-590 within classify_setting_report, filter
synthetic_timestamp results to those with normalized_utc present before calling
or_else, preserving fallback to the respective context source_timestamp; add a
test with a non-RFC-3339 EvaluatedAt and valid source_timestamp that asserts
evaluated_at is orderable.
| "artifactId": "mdm-events", | ||
| "family": "mdmEvents", | ||
| "sourceKind": "json", | ||
| "captureState": "parseFailed", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 6 'fn sources|captureState|ComplianceSourceInput|IntuneArtifactStatus' \
crates/cmtraceopen-parser/tests/intune_windows_compliance.rs \
crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rsRepository: adamgell/cmtraceopen
Length of output: 25527
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- fixture manifests and expected states ---'
for f in \
crates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/manifest.json \
crates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/*/manifest.json
do
[ -f "$f" ] || continue
if grep -qE '"captureState": "(parseFailed|unsupported)"' "$f"; then
echo "FILE: $f"
cat -n "$f" | sed -n '20,52p'
fi
done
printf '%s\n' '--- decode_bundle status handling ---'
cat -n crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs | sed -n '113,153p'
printf '%s\n' '--- fixture test expectations and scenario loader ---'
cat -n crates/cmtraceopen-parser/tests/intune_windows_compliance.rs | sed -n '80,145p'
rg -n -C 8 'malformed-and-unknown-schema|parseFailed|unsupported|captureState' \
crates/cmtraceopen-parser/tests/intune_windows_compliance.rs \
crates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/malformed-and-unknown-schemaRepository: adamgell/cmtraceopen
Length of output: 26940
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path
manifest_path = Path(
"crates/cmtraceopen-parser/tests/fixtures/intune/device/windows/compliance/"
"malformed-and-unknown-schema/manifest.json"
)
manifest = json.loads(manifest_path.read_text())
mapping = {
"captured": "Available",
"parseFailed": "ParseFailed",
"unsupported": "Unsupported",
}
for artifact in manifest["artifacts"]:
if artifact["artifactId"] in {"mdm-events", "mdm-report"}:
state = artifact["captureState"]
assert state in mapping
print(f'{artifact["artifactId"]}: {state} -> {mapping[state]}')
source = Path(
"crates/cmtraceopen-parser/src/intune/device/windows/compliance/sources.rs"
).read_text()
assert "None => (source.status.clone(), None)" in source
assert "Some(problem) => (problem.status, Some(problem.detail))" in source
print("decode_bundle preserves source.status when no DecodeProblem exists")
PYRepository: adamgell/cmtraceopen
Length of output: 303
Set both captureState values to captured. sources() maps them directly to ComplianceSourceInput.status, and decode_bundle preserves that status when decoding returns no problem. The current values can let the fixture pass without testing classification of malformed JSON and version 99.
🤖 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/fixtures/intune/device/windows/compliance/malformed-and-unknown-schema/manifest.json`
at line 29, Update both captureState entries in the malformed-and-unknown-schema
fixture manifest to "captured", preserving the fixture’s malformed JSON and
version 99 inputs so sources() and decode_bundle exercise their intended
classification paths.
| fn redaction_preserves_states_coverage_and_evidence() { | ||
| for scenario in SCENARIOS { | ||
| let loaded = load(scenario); | ||
| let projected = redacted_export_projection(&loaded.snapshot); | ||
| assert_eq!( | ||
| projected.aggregate, loaded.snapshot.aggregate, | ||
| "{scenario}: redaction must not touch the aggregate" | ||
| ); | ||
| assert_eq!( | ||
| projected.coverage, loaded.snapshot.coverage, | ||
| "{scenario}: redaction must not drop coverage" | ||
| ); | ||
| let ids = |snapshot: &ComplianceSnapshot| { | ||
| snapshot | ||
| .findings | ||
| .iter() | ||
| .map(|finding| (finding.finding_id.clone(), finding.evidence.clone())) | ||
| .collect::<Vec<_>>() | ||
| }; | ||
| assert_eq!( | ||
| ids(&projected), | ||
| ids(&loaded.snapshot), | ||
| "{scenario}: redaction must not change which evidence a finding cites" | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Verify all non-redacted snapshot fields.
This test does not compare reporting, local setting state and evidence, access decisions and evidence, or timestamps. A redaction regression can modify those fields and still pass.
Compare every non-PII field before and after projection. Preserve the existing checks for fields that must change.
🤖 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/intune_windows_compliance.rs` around lines
579 - 602, Expand redaction_preserves_states_coverage_and_evidence to compare
every non-PII ComplianceSnapshot field between projected and loaded.snapshot,
including reporting, local setting state and evidence, access decisions and
evidence, and timestamps. Preserve the existing aggregate, coverage, and finding
evidence assertions, while excluding only fields intentionally redacted.
Summary
Implements
cmtraceopen_parser::intune::device::windows::compliance— pure-Rust, wasm32-compatible four-phase analyzer for Intune Windows device compliance evaluation and reporting evidence.Supersedes the implementation on stale PR #449 (same module + 16-scenario fixture matrix + audit fixes), restacked onto current
mainso CI is not blocked by the unrelated company-portalstd::processpurity failure that failed Check & Test on the old base.Module structure
models.rssources.rsreducer.rsrules.rsredaction.rsInvariants preserved
Local verification
cargo test -p cmtraceopen-parser --test intune_windows_compliance→ 16 passedcargo test -p cmtraceopen-parser --test intune_skeleton_contract→ 26 passedcargo clippy -p cmtraceopen-parser --all-targets -- -D warnings→ cleanstd::process/ I/O in the compliance leafRelationship to open PRs
Test plan
-D warningsgreen on parser crateFixes #364
Summary by CodeRabbit
New Features
Bug Fixes
Tests