Skip to content

fix(devicepolicy): repair legacy Windows state permissions - #237

Merged
ashishkurmi merged 3 commits into
step-security:mainfrom
raysubham:fix/windows-device-policy-state-repair
Oct 26, 2026
Merged

ashishkurmi merged 3 commits into
step-security:mainfrom
raysubham:fix/windows-device-policy-state-repair

Conversation

@raysubham

@raysubham raysubham commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Device-policy state files written by older agents inherit their parent's DACL on Windows, so the strict metadata check rejects them. Reads then report no ownership, and writes, writability probes and clears fail.

  • Shared state preparation: read, write, probe and clear repair a correctly owned state file's DACL under the existing cacheMu and cross-process state lock before using it. Bytes, owner and all category/target records are preserved. A plain read takes the lock only when the file has legacy permissions; absent state creates nothing.
  • Minimum permissions: existing, owner-verified objects (state file, lock file, parent directory) are repaired with READ_CONTROL|WRITE_DAC only. Modify-only legacy permissions no longer need ownership-change rights. Fresh creation still assigns the owner. The final strict ACL is unchanged.
  • Clear safety: on Windows, a marker-based clear stops before changing configuration if the state store cannot be prepared (wrong owner, repair denied, lock busy). Missing or corrupt state keeps the existing cleanup.
  • Visibility: unexpected target-setup failures and swallowed state-read failures are logged at Warn, redacted. The expected no-active-user case stays at Debug.

macOS/Linux behavior is unchanged. No new dependencies, state-format changes or migration framework.

Type of change

  • Bug fix
  • Enhancement
  • Documentation

Testing

  • go test -race on affected packages, go vet, golangci-lint, gofmt, linux/darwin/windows builds, Windows test compilation
  • Native Windows secureuserfile permission tests under a limited-rights token, no skips (Modify-only repair, wrong-owner refusal)
  • macOS/Linux regression test: marker clear still removes the managed block when tracking state is unreadable
  • No secrets or credentials included (synthetic fixtures only)
  • Native Windows cache/policy lifecycle tests (legacy apply/repeat/clear, peer-lock refusal)
  • make smoke (45/45 checks)

Validated at 9612366: five native Windows lifecycle cases, including actual 1.15/1.16 state and the unchanged 1.17 failure control; 11 edge cases; limited-rights repair without skips; and broader native file-writer tests. macOS race tests and non-root Linux container regressions passed. All 19 CI checks passed. The isolated lifecycle driver uses synthetic policies and an in-process API, not a live backend rollout.

Related Issues

None.

State files left by older agents inherit their parent's DACL and fail the
strict metadata check, so reads report no ownership and writes, probes and
clears fail. Repair a correctly owned state file's DACL under the existing
cross-process lock before use, preserving its bytes, owner and records.

Existing verified objects (state, lock, parent directory) are repaired with
READ_CONTROL|WRITE_DAC only, so Modify-only legacy permissions no longer need
ownership-change rights. Fresh creation still assigns the owner.

On Windows, a marker-based clear now stops before changing configuration when
the state store cannot be prepared. Unexpected target-setup and swallowed
state-read failures are logged at Warn, redacted. macOS/Linux behavior is
unchanged.

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.

🔵 Needs a closer look

Security-sensitive Windows permission and clear-path changes still need the native lifecycle, peer-lock, and smoke validation marked pending in the PR description.

No open issues 🎉

What changed in this PR

This PR restores access to legacy Windows device-policy state by repairing permissions on owner-verified files without changing their contents or ownership.

Changes:

  • Adds permission-only repair for existing state files, lock files, and parent directories.
  • Integrates locked state preparation into cache operations and blocks marker-based clears when Windows state is unreadable.
  • Adds redacted warnings and regression tests for permission repair, state preservation, and clear behavior.
File Description
internal/​secureuserfile/​file.go Adds metadata repair and separates existing-object repair from creation.
internal/​secureuserfile/​file_windows.go Repairs DACLs without requesting ownership-change rights.
internal/​secureuserfile/​file_windows_test.go Tests limited-rights repair, ownership checks, and content preservation.
internal/​secureuserfile/​file_unix.go Delegates repair to existing POSIX metadata handling.
internal/​devicepolicy/​reconcile.go Stops marker clears when Windows state cannot be read safely.
internal/​devicepolicy/​cache.go Adds shared state preparation, locked read repair, and redacted warnings.
internal/​devicepolicy/​cache_windows_test.go Adds legacy-state lifecycle, peer-lock, and wrong-owner tests.
internal/​devicepolicy/​cache_test.go Adds a non-Windows unreadable-state clear regression test.
cmd/​stepsecurity-dev-machine-guard/​main.go Warns on unexpected target-setup failures with redaction.
cmd/​stepsecurity-dev-machine-guard/​main_devicepolicy_test.go Tests warning visibility and secret redaction.

Review effort: Balanced

@ashishkurmi
ashishkurmi added this pull request to the merge queue Oct 26, 2026
Merged via the queue into step-security:main with commit cd93eee Oct 26, 2026
19 checks passed
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.

3 participants