Skip to content

fix(baselines): make WS2022 baselines readable on standalone servers - #111

Merged
Amir Bredy (ABMFST) merged 4 commits into
mainfrom
fix/ws2022-readable-baselines
Aug 6, 2026
Merged

fix(baselines): make WS2022 baselines readable on standalone servers#111
Amir Bredy (ABMFST) merged 4 commits into
mainfrom
fix/ws2022-readable-baselines

Conversation

@ABMFST

@ABMFST Amir Bredy (ABMFST) commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Convert all 215 legacy Microsoft.Windows/CSP Policy Result paths across the three bundled Windows Server 2022 baselines to dedicated Registry, AuditPolicy, UserRightsAssignment, or AccountPolicy providers.
  • Normalize registry addressing/types, preserve the WS2022 desired values, and restore the final empty-user-right assertions as list-aware CEL checks.
  • Add deterministic, pinned derivation/repair tooling, committed provenance, focused regression coverage, and full-history checkout for the evidence tests.
  • Leave package versions and package-lock.json unchanged.

Support and validation caveat

OSConfig security baselines officially target Windows Server 2025. Windows Server 2022 remains a best-effort, not officially supported OSConfig baseline target.

The live smoke used Windows Server 2025 with oscfg 1.3.12-preview5, not Windows Server 2022:

Baseline Compliant Non-compliant Read errors
Shipped WS2022 workgroup profile 171 29
Repaired profile 200 2 0

This demonstrates improved provider readability on that WS2025 host. It is not native WS2022 validation, does not claim WS2022 support, and does not claim enforcement. The two non-compliant results were genuine findings on the smoke host rather than read failures.

Validation

Final branch, including review-fix commit 1c4b18d:

  • node scripts/ws2022-baseline-repair/derive-maps.mjs --check
  • node scripts/ws2022-baseline-repair/repair-ws2022-baselines.mjs --check
  • 146 focused repair + desktop regression tests passed with provenance checks required
  • Targeted ESLint: 0 errors (one pre-existing max-lines warning)
  • Earlier final-branch validation: 1,789 tests, lint with 0 errors, desktop build, audit with 0 vulnerabilities, and public packaging checks
  • 215 CSP conversions; 0 residual CSP/Policy Result paths
  • Package manifests and lockfile unchanged

Amir Bredy and others added 3 commits August 5, 2026 13:47
…lone servers

The three bundled WS2022 role baselines shipped in their original generated
form: 71-73 rules per profile addressed audit policy, user rights and account
policy through Microsoft.Windows/CSP at ./Vendor/MSFT/Policy/Result/..., which
only resolves through OMA-DM. On a standalone (non-MDM-enrolled) server those
settings come back unread instead of compliant/non-compliant. The profiles also
used colon-less registry hive prefixes, legacy value-type aliases, and ambiguous
schema: {} compliance blocks.

WS2025 hit the identical defect and was repaired in #82/#93. This change applies
the same reviewed mechanism to WS2022 via a deterministic converter:

- Every Policy/Result CSP rule moves to a dedicated AuditPolicy (exact
  subcategory GUID), UserRightsAssignment (exact Se* right) or AccountPolicy
  (exact policy name) provider. Residual CSP: zero in all three profiles.
- Registry keyPaths gain the required hive colon and canonical REG_* value
  types; five array-in-Dword payloads are reshaped per the WS2025 contract for
  the identical keyPath/valueName.
- schema: {} blocks become explicit CEL expression/template pairs. Ambiguous
  ones become the WS2025 informational form rather than an invented assertion;
  each downgrade is recorded with its reason.

WS2022 desired values are authoritative - only mechanism and addressing are
borrowed from WS2025. The only value changes are provider-mandated reshapes,
all enumerated in conversion-report.json.

Counts: Member Server 257 -> 259, Domain Controller 242 -> 244, Workgroup
Member 200 -> 202. The +2 delta is the composite AccountLockoutPolicy CSP
string expanding into LockoutDuration/LockoutThreshold/LockoutReset.

The mapping tables are committed as reviewable JSON extracted from the WS2025
repair commits; derive-maps.mjs --check proves they still match. The conversion
report is committed too, because CI checks out shallow and the tests must
validate parity without reading pre-repair history.

OSConfig security baseline support is officially Windows Server 2025 only, so
WS2022 remains best-effort; the README states this and the remaining
limitations honestly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…provenance

Follow-up to 91dd551, addressing independent review findings.

Restore real assertions on "must be unassigned" user rights. Seven controls per
member profile and six on the domain controller carried the scalar schema
{"oneOf":[{"const":""},{"type":"null"}]} with a desired value of "", i.e. the
right must be granted to nobody. Because UserRightsAssignment reads back a list
of principals, the previous change downgraded them to expression: 'true', which
silently dropped a real security constraint. They are now restated over the list
as `value == null || value.size() == 0`. CEL's || short-circuits so size() never
touches an unset value, and size() on a UserRightsAssignment principal list is
already exercised in packages/core/src/import-export/index.test.ts.
translatePrincipalListSchema() returns null for any other shape, so an
unreviewed schema still surfaces as an explicit downgrade. assertionDowngrades
is now empty in all three profiles; assertionRestatements is 7/6/7.

Pin all provenance to full SHAs reachable from origin/main. 6fb3052 was a
branch-local commit and is replaced by 37ab26a, the main-reachable
schema-to-expression translation; the derived tables are byte-identical, which
confirms the substitution. derive-maps.mjs now loads both the before and after
artifacts with `git show <full SHA>`, never the working tree, refuses any ref
that is not a 40-character SHA, and verifies each pin resolves to itself and is
an ancestor of origin/main before deriving anything. The map _provenance blocks
carry providerBeforeCommit/providerAfterCommit and
schemaBeforeCommit/schemaAfterCommit instead of a mutable HEAD reference.

Break the circular tests. The old suite asserted generated output against the
map that generated it, and a provenance regex silently matched the string
"undefined". The suite now asserts the exact pinned SHAs and their ancestry,
independently parses the pinned before/after baselines and re-derives every CSP
map target address (audit subcategory GUID, Se* right name, account policy name)
and every schema-to-expression pair from that evidence, and pins explicit
representative mappings and desired values. It also proves the pinned schema
commit has zero Policy/Result CSP rules and exactly the five reviewed
Policy/Config residuals.

Make --report read-only as documented. Writes now happen only when neither
--check nor --report is given, and a filesystem test asserts the three
baselines, both maps and the conversion report keep their content, size and
mtime across a --report run that still prints the full summary.

Record the live smoke result as labelled evidence in conversion-report.json and
the README: the shipped workgroup-member profile returned 171 compliant with 29
read errors, the repaired profile 200 compliant, 2 genuinely non-compliant and 0
read errors, under oscfg 1.3.12. That run was executed on a Windows Server 2025
host and is explicitly not native Windows Server 2022 validation.

The CI vitest job now checks out with fetch-depth: 0 so the evidence
re-derivation tests can run; they skip on a shallow local clone but are
mandatory whenever CI is set.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…view5

Review correction. The live smoke run of ws2022-workgroup-member was performed
with oscfg 1.3.12-preview5, not the released 1.3.12. The exact preview build is
now recorded everywhere the smoke provenance appears: LIVE_SMOKE in the
converter, _provenance.liveSmoke in the deterministically regenerated
conversion-report.json, the README evidence table, and the test expectation.

Evidence only; no baseline, mapping table or assertion changes. The three
ws2022-*.osc.yaml profiles are byte-identical, and --check passes for both the
converter and the map derivation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 5, 2026 23:53
Comment thread scripts/ws2022-baseline-repair/repair-ws2022-baselines.mjs Fixed
Comment thread scripts/ws2022-baseline-repair/derive-maps.mjs Fixed

Copilot AI 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.

Pull request overview

Repairs the bundled Windows Server 2022 security baseline manifests so standalone (non‑MDM) servers can read and evaluate all controls, by replacing legacy Policy/Result CSP addressing with dedicated providers and converting legacy schema compliance blocks to explicit CEL expression/template assertions. This fits into the baseline catalog + regression suite by ensuring shipped manifests remain readable/auditable and the derivation is reproducible from pinned, reviewed evidence commits.

Changes:

  • Add deterministic WS2022 repair tooling (pinned evidence commits, derived mapping tables, conversion report) and a conversion script that rewrites the three WS2022 role baselines.
  • Add focused regression coverage validating provider payload shapes, rule counts, CSP elimination, and preserved desired values; update WS2022 baseline catalog metadata accordingly.
  • Update CI checkout depth to allow evidence re-derivation tests to run (requires full history) and record the change in changelogs.

Reviewed changes

Copilot reviewed 11 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
scripts/ws2022-baseline-repair/derive-maps.mjs Derives provider/schema translation tables from pinned WS2025 evidence commits; enforces provenance constraints.
scripts/ws2022-baseline-repair/repair-ws2022-baselines.mjs Deterministic converter that repairs WS2022 baselines and emits a structured conversion report.
scripts/ws2022-baseline-repair/repair-ws2022-baselines.test.mjs Unit tests that re-derive and validate mapping claims directly from pinned commits (non-circular).
scripts/ws2022-baseline-repair/README.md Documents rationale, conversion policy, evidence pins, rerun steps, and limitations.
scripts/ws2022-baseline-repair/csp-provider-map.json Committed, reviewed mapping of WS2022-used Policy/Result OMA-URIs to dedicated providers/addresses.
scripts/ws2022-baseline-repair/schema-expression-map.json Committed, reviewed mapping of legacy JSON-schema compliance shapes to CEL expression/template pairs.
scripts/ws2022-baseline-repair/conversion-report.json Committed provenance + per-profile conversion details to validate shipped outputs without git archaeology.
public/_baselines/ws2022-domain-member.osc.yaml Shipped WS2022 baseline updated to use dedicated providers + CEL assertions.
public/_baselines/ws2022-domain-controller.osc.yaml Shipped WS2022 baseline updated to use dedicated providers + CEL assertions.
public/_baselines/ws2022-workgroup-member.osc.yaml Shipped WS2022 baseline updated to use dedicated providers + CEL assertions.
apps/desktop/src/data/ws2022-baselines.test.ts Desktop-side regression tests asserting repaired WS2022 baseline shape, counts, provider payload validity, and value preservation.
apps/desktop/src/data/baseline-catalog.ts Updates WS2022 baseline catalog metadata (resource counts; removes upstream githubUrl since local manifests diverge).
.github/workflows/pr-check.yml Switches CI test checkout to full history so pinned-evidence tests can run.
CHANGELOG.md Adds Unreleased entry describing the WS2022 baseline repair and its caveats.
docs/src/changelog.md Adds Unreleased docs changelog entry describing the WS2022 baseline repair and its caveats.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/ws2022-baseline-repair/derive-maps.mjs Outdated
Comment thread apps/desktop/src/data/ws2022-baselines.test.ts Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 6, 2026 00:06

Copilot AI 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.

Pull request overview

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

Suppressed comments (2)

apps/desktop/src/data/ws2022-baselines.test.ts:284

  • report.sourceRuleNames is an array; using Object.keys(...).length is an indirect way to assert its size and could behave unexpectedly if the array ever becomes sparse. Use the array’s .length for clarity and correctness.
    it("agrees with the committed conversion report", () => {
      expect(report.sourceRules).toBe(expected.sourceRules);
      expect(report.sourceCsp).toBe(expected.sourceCsp);
      expect(report.convertedCsp).toBe(expected.sourceCsp);
      expect(Object.keys(report.sourceRuleNames).length).toBe(expected.sourceRules);
    });

apps/desktop/src/data/ws2022-baselines.test.ts:351

  • This test name says “four dedicated providers” but the assertion only allows three (AccountPolicy, AuditPolicy, UserRightsAssignment). Since report.conversions is CSP→dedicated-provider conversions (and doesn’t include Registry), the name should match what’s asserted to avoid confusion for future maintainers.
    it("only converts to the four dedicated providers", () => {
      const targets = new Set(report.conversions.map((c) => c.to));
      expect([...targets].sort()).toEqual([ACCOUNT, AUDIT, USER_RIGHTS].sort());
    });

@ABMFST
Amir Bredy (ABMFST) merged commit 17c8bc5 into main Aug 6, 2026
12 checks passed
@ABMFST
Amir Bredy (ABMFST) deleted the fix/ws2022-readable-baselines branch August 6, 2026 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants