diff --git a/.gitattributes b/.gitattributes index 6f87ca1fe..62ae670ee 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,5 @@ # Real-world log fixtures are byte-sensitive (UTF-8 BOM + CRLF); never normalize them. src-tauri/tests/fixtures/** -text +# SCCM parser fixtures are byte-sensitive evidence; never normalize them. +crates/cmtraceopen-parser/tests/fixtures/** -text diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/README.md b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/README.md index b45b676ea..290069023 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/README.md +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/README.md @@ -1,11 +1,12 @@ # Synthetic SCCM server intake fixtures -These fixtures prepare issue #335 while #318 owns the shared SCCM schema. They -are intentionally not connected to a Rust test target yet. Every value is -synthetic, deterministic, and privacy-safe: +These fixtures prepare issue #335 while #318 owns the shared SCCM schema. A +focused Rust fixture-contract test enforces their site-code, rotation-path, and +byte-integrity invariants, but no production native reader consumes them yet. +Every value is synthetic, deterministic, and privacy-safe: -- permitted topology labels are `LAB-CM01`, `LAB-MP01`, `LAB-DP01`, and - `CONTOSO`; +- permitted topology host labels are `LAB-CM01`, `LAB-MP01`, and `LAB-DP01`; +- the exact synthetic three-character site code is `LAB`; - raw source paths are replaced by `REDACTED_*` markers; - configured roots use deterministic opaque `synthetic:path:*` fingerprints; - every manifest declares `syntheticFixture: true` and `proposalOnly: true`; diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/absent-dp/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/absent-dp/manifest.json index eb6b2b158..17e21458b 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/absent-dp/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/absent-dp/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-CM01", "siteCode": "CONTOSO", "rolesObserved": ["siteServer"] }, + "topology": { "captureHost": "LAB-CM01", "siteCode": "LAB", "rolesObserved": ["siteServer"] }, "artifacts": [ { "artifactId": "dp-distribution-absent-candidate", "producerRole": "siteServer", "producerHostHandle": "synthetic:host:site-01", "workflowSubject": { "role": "distributionPoint", "basis": "incidentScopeOnly" }, "sourceId": "server-dp-distribution", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_DEFAULT_DP_CANDIDATE", "originalBasename": "distmgr.log", "configuredPathProvenance": { "state": "defaultCandidate", "pathFingerprint": "synthetic:path:dp-default" }, "rotation": { "kind": "current", "lineageId": "dp-distribution-default" }, "captureState": "absent", "collectedUtc": "2026-07-30T00:04:00Z", "relativePath": null, "bytesCopied": 0 } ] diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/access-denied-mp/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/access-denied-mp/manifest.json index 5b5bea22f..cb93c009e 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/access-denied-mp/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/access-denied-mp/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-MP01", "siteCode": "CONTOSO", "rolesObserved": ["managementPoint"] }, + "topology": { "captureHost": "LAB-MP01", "siteCode": "LAB", "rolesObserved": ["managementPoint"] }, "artifacts": [ { "artifactId": "mp-policy-access-denied", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.log", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "current", "lineageId": "mp-policy-access" }, "captureState": "accessDenied", "collectionDetail": "synthetic permission denial", "collectedUtc": "2026-07-30T00:05:00Z", "relativePath": null, "bytesCopied": 0 } ] diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/capped-sup/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/capped-sup/manifest.json index a1fcf9c5e..300ba634f 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/capped-sup/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/capped-sup/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-CM01", "siteCode": "CONTOSO", "rolesObserved": ["siteServer"] }, + "topology": { "captureHost": "LAB-CM01", "siteCode": "LAB", "rolesObserved": ["siteServer"] }, "artifacts": [ { "artifactId": "sup-sync-capped", "producerRole": "siteServer", "producerHostHandle": "synthetic:host:site-01", "workflowSubject": { "role": "softwareUpdatePoint", "instanceHandle": "synthetic:subject:sup-01" }, "sourceId": "server-sup-sync", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_SITE_SUP_CONTROL_ROOT", "originalBasename": "wsyncmgr.log", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:site-sup-control" }, "rotation": { "kind": "current", "lineageId": "sup-sync-cap" }, "captureState": "capped", "encoding": "utf-8", "collectionLimit": { "byteLimit": 64, "limitApplied": true }, "bytesCopied": 64, "truncated": true, "fragmentComplete": false, "collectedUtc": "2026-07-30T00:06:00Z", "relativePath": "evidence/sccm/server/site-server/server-sup-sync/subject-software-update-point/instance-17eae15500d8968f/root-b11afca548220198/current/wsyncmgr.log" } ] diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/collision-same-basename-configured-roots/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/collision-same-basename-configured-roots/manifest.json index 2bbf6cbcc..32d856c01 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/collision-same-basename-configured-roots/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/collision-same-basename-configured-roots/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-MP01", "siteCode": "CONTOSO", "rolesObserved": ["managementPoint"] }, + "topology": { "captureHost": "LAB-MP01", "siteCode": "LAB", "rolesObserved": ["managementPoint"] }, "artifacts": [ { "artifactId": "mp-policy-root-a-current", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_CONFIGURED_MP_ROOT_A", "originalBasename": "MP_GetPolicy.log", "configuredPathProvenance": { "state": "configured", "pathClass": "nonDefault", "pathFingerprint": "synthetic:path:mp-root-a" }, "rotation": { "kind": "current", "lineageId": "mp-policy-root-a" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:10:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/root-7d4a9c2e/current/MP_GetPolicy.log", "bytesCopied": 173 }, { "artifactId": "mp-policy-root-b-current", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_CONFIGURED_MP_ROOT_B", "originalBasename": "MP_GetPolicy.log", "configuredPathProvenance": { "state": "configured", "pathClass": "nonDefault", "pathFingerprint": "synthetic:path:mp-root-b" }, "rotation": { "kind": "current", "lineageId": "mp-policy-root-b" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:10:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/root-b83f10d6/current/MP_GetPolicy.log", "bytesCopied": 172 } diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/complete-multi-role/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/complete-multi-role/manifest.json index 268f6e673..515dd4a60 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/complete-multi-role/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/complete-multi-role/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-CM01", "siteCode": "CONTOSO", "rolesObserved": ["siteServer", "managementPoint", "distributionPoint", "softwareUpdatePoint"] }, + "topology": { "captureHost": "LAB-CM01", "siteCode": "LAB", "rolesObserved": ["siteServer", "managementPoint", "distributionPoint", "softwareUpdatePoint"] }, "artifacts": [ { "artifactId": "sitecomp-current", "producerRole": "siteServer", "producerHostHandle": "synthetic:host:site-01", "sourceId": "server-sitecomp", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_SITE_ROOT", "originalBasename": "sitecomp.log", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:site-default" }, "rotation": { "kind": "current", "lineageId": "sitecomp-lab" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:00:00Z", "relativePath": "evidence/sccm/server/site-server/server-sitecomp/current/sitecomp.log", "bytesCopied": 171 }, { "artifactId": "mp-policy-current", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.log", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "current", "lineageId": "mp-policy-lab" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:00:01Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/current/MP_GetPolicy.log", "bytesCopied": 184 }, diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/configured-nondefault-path/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/configured-nondefault-path/manifest.json index 58b4a187e..3986a09b4 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/configured-nondefault-path/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/configured-nondefault-path/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-MP01", "siteCode": "CONTOSO", "rolesObserved": ["managementPoint"] }, + "topology": { "captureHost": "LAB-MP01", "siteCode": "LAB", "rolesObserved": ["managementPoint"] }, "artifacts": [ { "artifactId": "mp-policy-configured", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_CONFIGURED_NONDEFAULT_ROOT", "originalBasename": "MP_GetPolicy.log", "configuredPathProvenance": { "state": "configured", "pathClass": "nonDefault", "pathFingerprint": "synthetic:path:mp-configured-a" }, "defaultCandidateState": "absentCandidateOnly", "rotation": { "kind": "current", "lineageId": "mp-policy-configured" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:01:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/current/MP_GetPolicy.log", "bytesCopied": 183 } ] diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/multiline/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/multiline/manifest.json index 1bae87c0a..64b91d9bb 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/multiline/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/multiline/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-MP01", "siteCode": "CONTOSO", "rolesObserved": ["managementPoint"] }, + "topology": { "captureHost": "LAB-MP01", "siteCode": "LAB", "rolesObserved": ["managementPoint"] }, "artifacts": [ { "artifactId": "mp-policy-multiline", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.log", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "current", "lineageId": "mp-policy-multiline" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:03:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/current/MP_GetPolicy.log", "bytesCopied": 207 } ] diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/evidence/sccm/server/management-point/server-mp-policy/lo_/MP_GetPolicy.log.lo_ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/evidence/sccm/server/management-point/server-mp-policy/lo_/MP_GetPolicy.lo_ similarity index 100% rename from crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/evidence/sccm/server/management-point/server-mp-policy/lo_/MP_GetPolicy.log.lo_ rename to crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/evidence/sccm/server/management-point/server-mp-policy/lo_/MP_GetPolicy.lo_ diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/manifest.json index 2961af624..48051361c 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/rotations/manifest.json @@ -4,11 +4,11 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-MP01", "siteCode": "CONTOSO", "rolesObserved": ["managementPoint"] }, + "topology": { "captureHost": "LAB-MP01", "siteCode": "LAB", "rolesObserved": ["managementPoint"] }, "artifacts": [ { "artifactId": "mp-policy-ts-20260729-235700", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.log.20260729-235700", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "timestamped", "value": "20260729-235700", "lineageId": "mp-policy-rotation" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:02:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/timestamped-20260729-235700/MP_GetPolicy.log.20260729-235700", "bytesCopied": 182 }, { "artifactId": "mp-policy-current", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.log", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "current", "lineageId": "mp-policy-rotation" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:02:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/current/MP_GetPolicy.log", "bytesCopied": 178 }, - { "artifactId": "mp-policy-lo", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.log.lo_", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "lo_", "lineageId": "mp-policy-rotation" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:02:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/lo_/MP_GetPolicy.log.lo_", "bytesCopied": 177 }, + { "artifactId": "mp-policy-lo", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.lo_", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "lo_", "lineageId": "mp-policy-rotation" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:02:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/lo_/MP_GetPolicy.lo_", "bytesCopied": 177 }, { "artifactId": "mp-policy-numbered-2", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-policy", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_MP_ROOT", "originalBasename": "MP_GetPolicy.log.2", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:mp-default" }, "rotation": { "kind": "numbered", "value": 2, "lineageId": "mp-policy-rotation" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:02:00Z", "relativePath": "evidence/sccm/server/management-point/server-mp-policy/numbered-2/MP_GetPolicy.log.2", "bytesCopied": 179 } ] } diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/skipped-iis/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/skipped-iis/manifest.json index e206619f2..74e1ebf47 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/skipped-iis/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/skipped-iis/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-MP01", "siteCode": "CONTOSO", "rolesObserved": ["managementPoint"] }, + "topology": { "captureHost": "LAB-MP01", "siteCode": "LAB", "rolesObserved": ["managementPoint"] }, "artifacts": [ { "artifactId": "mp-iis-skipped", "producerRole": "managementPoint", "producerHostHandle": "synthetic:host:mp-01", "sourceId": "server-mp-iis", "sourceKind": "iisW3c", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_IIS_EXPORT", "originalBasename": "u_ex_synthetic.log", "configuredPathProvenance": { "state": "notRequested", "pathFingerprint": "synthetic:path:iis-not-requested" }, "rotation": { "kind": "providerDefined", "lineageId": "mp-iis-supplement" }, "captureState": "skipped", "skipReason": "optional supplemental source not requested", "collectedUtc": "2026-07-30T00:07:00Z", "relativePath": null, "bytesCopied": 0 } ] diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsorted-manifest/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsorted-manifest/manifest.json index 12d37be4e..28e9c338f 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsorted-manifest/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsorted-manifest/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-CM01", "siteCode": "CONTOSO", "rolesObserved": ["siteServer", "managementPoint"] }, + "topology": { "captureHost": "LAB-CM01", "siteCode": "LAB", "rolesObserved": ["siteServer", "managementPoint"] }, "inputOrderIsDeliberatelyUnsorted": true, "artifacts": [ { "artifactId": "z-site-status", "producerRole": "siteServer", "producerHostHandle": "synthetic:host:site-01", "sourceId": "server-status", "sourceKind": "ccmLog", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_SITE_ROOT_B", "originalBasename": "statmgr.log", "configuredPathProvenance": { "state": "configured", "pathFingerprint": "synthetic:path:z-site" }, "rotation": { "kind": "current", "lineageId": "site-status-z" }, "captureState": "captured", "encoding": "utf-8", "collectionLimit": { "byteLimit": 4096, "limitApplied": false }, "collectedUtc": "2026-07-30T00:09:00Z", "relativePath": "evidence/sccm/server/site-server/server-status/current/statmgr.log", "bytesCopied": 167 }, diff --git a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsupported-db-supplement/manifest.json b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsupported-db-supplement/manifest.json index 998d66d2c..0455b1202 100644 --- a/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsupported-db-supplement/manifest.json +++ b/crates/cmtraceopen-parser/tests/fixtures/sccm/server/intake/unsupported-db-supplement/manifest.json @@ -4,7 +4,7 @@ "proposalOnly": true, "privacy": { "synthetic": true, "rawPaths": "redacted" }, "bundleRole": "server", - "topology": { "captureHost": "LAB-CM01", "siteCode": "CONTOSO", "rolesObserved": ["siteServer"] }, + "topology": { "captureHost": "LAB-CM01", "siteCode": "LAB", "rolesObserved": ["siteServer"] }, "artifacts": [ { "artifactId": "unknown-db-export", "producerRole": "unclassified", "producerHostHandle": null, "sourceId": "unknown-db-supplement", "sourceKind": "unknown", "sourceVersion": "5.00.TEST", "originalPath": "REDACTED_UNSUPPORTED_EXPORT", "originalBasename": "synthetic-db-export.txt", "configuredPathProvenance": { "state": "supplied", "pathFingerprint": "synthetic:path:unsupported-db" }, "rotation": { "kind": "none", "lineageId": "unknown-db-export" }, "captureState": "unsupported", "unsupportedReason": "no approved server source contract", "collectedUtc": "2026-07-30T00:08:00Z", "relativePath": null, "bytesCopied": 0 } ] diff --git a/crates/cmtraceopen-parser/tests/sccm_server_intake_fixture_contract.rs b/crates/cmtraceopen-parser/tests/sccm_server_intake_fixture_contract.rs new file mode 100644 index 000000000..62aba00bb --- /dev/null +++ b/crates/cmtraceopen-parser/tests/sccm_server_intake_fixture_contract.rs @@ -0,0 +1,108 @@ +use serde_json::Value; + +fn server_intake_root() -> std::path::PathBuf { + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/sccm/server/intake") +} + +fn server_intake_manifests() -> Vec<(String, Value)> { + let mut scenario_dirs = std::fs::read_dir(server_intake_root()) + .expect("server intake fixture root is readable") + .map(|entry| { + entry + .expect("server intake directory entry is readable") + .path() + }) + .filter(|path| path.is_dir()) + .collect::>(); + scenario_dirs.sort(); + + scenario_dirs + .into_iter() + .map(|scenario_dir| { + let scenario = scenario_dir + .file_name() + .expect("scenario directory has a name") + .to_string_lossy() + .into_owned(); + let contents = std::fs::read_to_string(scenario_dir.join("manifest.json")) + .unwrap_or_else(|error| panic!("{scenario}: manifest is readable: {error}")); + let manifest = serde_json::from_str(&contents).unwrap_or_else(|error| { + panic!("{scenario}: manifest contains valid JSON: {error}") + }); + (scenario, manifest) + }) + .collect() +} + +#[test] +fn server_intake_uses_canonical_site_and_rotation_contracts() { + let manifests = server_intake_manifests(); + assert_eq!(manifests.len(), 11, "server intake scenario matrix changed"); + + let mut failures = Vec::new(); + for (scenario, manifest) in &manifests { + let site_code = manifest["topology"]["siteCode"] + .as_str() + .expect("server intake topology has a site code"); + if site_code.len() != 3 + || !site_code + .bytes() + .all(|byte| byte.is_ascii_uppercase() || byte.is_ascii_digit()) + { + failures.push(format!( + "{scenario}: siteCode must match ^[A-Z0-9]{{3}}$, got {site_code}" + )); + } + } + + let rotations = manifests + .iter() + .find(|(scenario, _)| scenario == "rotations") + .map(|(_, manifest)| manifest) + .expect("server intake has a rotations scenario"); + let rollover = rotations["artifacts"] + .as_array() + .expect("rotation artifacts are an array") + .iter() + .find(|artifact| artifact["rotation"]["kind"] == "lo_") + .expect("rotation corpus has a .lo_ artifact"); + + let basename = rollover["originalBasename"] + .as_str() + .expect("rollover artifact has an original basename"); + if basename != "MP_GetPolicy.lo_" { + failures.push(format!( + "rotations: standard ConfigMgr rollover basename must be MP_GetPolicy.lo_, got {basename}" + )); + } + + let relative_path = rollover["relativePath"] + .as_str() + .expect("captured rollover has a relative path"); + if !relative_path.ends_with("/MP_GetPolicy.lo_") { + failures.push(format!( + "rotations: rollover relativePath must end in /MP_GetPolicy.lo_, got {relative_path}" + )); + } + let fixture_path = server_intake_root().join("rotations").join(relative_path); + if !fixture_path.is_file() { + failures.push(format!( + "rotations: manifest relativePath does not resolve to a fixture: {}", + fixture_path.display() + )); + } else { + let bytes_copied = rollover["bytesCopied"] + .as_u64() + .expect("captured rollover records bytesCopied"); + let actual_bytes = std::fs::metadata(&fixture_path) + .expect("rollover fixture metadata is readable") + .len(); + if bytes_copied != actual_bytes { + failures.push(format!( + "rotations: bytesCopied {bytes_copied} does not match fixture length {actual_bytes}" + )); + } + } + + assert!(failures.is_empty(), "{}", failures.join("\n")); +} diff --git a/docs/sccm/preparation/issue-335-server-intake.md b/docs/sccm/preparation/issue-335-server-intake.md index cd6b66fea..4c2551352 100644 --- a/docs/sccm/preparation/issue-335-server-intake.md +++ b/docs/sccm/preparation/issue-335-server-intake.md @@ -169,10 +169,10 @@ Deferred native tests must make the write/privacy boundaries observable: provider-defined, then none. Timestamped values sort ascending by valid `YYYYMMDD-HHMMSS`; numbered values sort by descending integer; remaining ties use lineage ID, basename, capture state, relative path, and artifact ID - in binary-stable lexical order. Canonical spellings are `.log.lo_`, `.log.N`, - and `.log.YYYYMMDD-HHMMSS`. This is serialization order only: intended - lineage/record chronology is evaluated separately and is never inferred from - array position. + in binary-stable lexical order. Canonical spellings are replacement-extension + `.lo_`, numbered `.log.N`, and `.log.YYYYMMDD-HHMMSS`. This is serialization + order only: intended lineage/record chronology is evaluated separately and + is never inferred from array position. - For every admitted complete record, its authoritative UTC instant is derived only from a syntactically valid date/time/offset and must be less than or equal to `collectedUtc` with zero synthetic tolerance. A timestamped diff --git a/docs/superpowers/plans/2026-07-30-sccm-server-intake-and-core.md b/docs/superpowers/plans/2026-07-30-sccm-server-intake-and-core.md index 55ad7dc2e..2c65230ab 100644 --- a/docs/superpowers/plans/2026-07-30-sccm-server-intake-and-core.md +++ b/docs/superpowers/plans/2026-07-30-sccm-server-intake-and-core.md @@ -21,7 +21,7 @@ - Analyze only complete logical records. A partial first/last rotation record, malformed record, unknown profile version, or invalid timestamp offset may create a coverage/parse gap or low-confidence symptom, never a terminal role diagnosis. - Findings name the last evidenced good hop and a bounded next artifact request. An error-looking server record alone cannot establish a root cause for a client. - The new SCCM Server dev environment is a validation source, not a blocker. Parser/corpus work proceeds against synthetic inputs. Native acceptance remains pending until the lab is authorized and exercised. -- Never commit live site names, host names, users, domain names, certificates, URLs, database names, package IDs, client identifiers, credentials, or customer logs. Use LAB-CM01, LAB-MP01, LAB-DP01, CONTOSO, and synthetic keys. +- Never commit live site names, host names, users, domain names, certificates, URLs, database names, package IDs, client identifiers, credentials, or customer logs. Use LAB-CM01, LAB-MP01, LAB-DP01, the three-character site code LAB, and synthetic keys. --- @@ -100,7 +100,7 @@ The server manifest needs enough information to interpret evidence without query "topology": { "captureHost": "LAB-CM01", "rolesObserved": ["siteServer", "managementPoint"], - "siteCode": "CONTOSO" + "siteCode": "LAB" }, "artifacts": [{ "artifactId": "server-mp-get-policy",