Skip to content

Windows MDM validation fixes#46029

Merged
getvictor merged 7 commits into
mainfrom
victor/42219-42224-validation
May 24, 2026
Merged

Windows MDM validation fixes#46029
getvictor merged 7 commits into
mainfrom
victor/42219-42224-validation

Conversation

@getvictor

@getvictor getvictor commented May 21, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #42219
Resolves #42224

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced Windows MDM profile validation: uploads that are non-XML, empty, or lack required SyncML top-level elements are rejected; LocURI values that are empty, start with /, contain .. path traversal, or are otherwise malformed are now rejected (whitespace-trimmed), preventing later device deployment failures.

Review Change Stack

@getvictor getvictor requested a review from Copilot May 21, 2026 22:04
@getvictor

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@getvictor

Copy link
Copy Markdown
Member Author

/agentic_review

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented May 21, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Remediation recommended

1. Traversal check bypass ✓ Resolved 🐞 Bug ⛨ Security
Description
validateLocURIFormat only blocks the literal substring "../", so a LocURI containing a ".."
traversal segment without a trailing slash (e.g. "./Device/Vendor/..") is accepted. This weakens the
new path-traversal protection and can allow invalid or ambiguously-interpreted LocURIs through
upload-time validation.
Code

server/fleet/windows_mdm.go[R244-255]

Evidence
The new validation explicitly intends to block path traversal, but it only checks for the specific
substring "../" and therefore does not reject a ".." segment at end-of-string.

server/fleet/windows_mdm.go[240-256]
server/fleet/windows_mdm_test.go[812-915]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`validateLocURIFormat` currently rejects traversal only when the trimmed LocURI contains the substring `"../"`. This misses cases where `..` appears as the final path segment (e.g. `./Device/Vendor/..`), which is still a traversal segment.
## Issue Context
This validation is meant to reject OMA-DM LocURIs with traversal sequences at upload time.
## Fix Focus Areas
- server/fleet/windows_mdm.go[240-256]
### Implementation notes
- After trimming and confirming the `./` prefix, validate traversal by parsing path segments:
- Strip the leading `./`.
- Split on `/`.
- Reject if any segment equals `".."`.
- Add/extend unit tests to cover `./Device/Vendor/..` (and optionally `./Device/..`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Top-level error text outdated ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
ValidateUserProvided’s updated doc/comment says supported top-level elements include <Replace>,
<Add>, <Exec>, and <Atomic>, but the validation error for unsupported top-level elements still says
only <Replace> or <Add> are allowed. This creates confusing, user-visible guidance that contradicts
the actual validator behavior and documentation.
Code

server/fleet/windows_mdm.go[R85-89]

Evidence
The validator documentation claims four supported top-level elements, but the actual error returned
when an unsupported top-level element is found still states only Replace/Add are allowed.

server/fleet/windows_mdm.go[82-90]
server/fleet/windows_mdm.go[167-175]
server/fleet/windows_mdm_test.go[792-810]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The public-facing comment for `ValidateUserProvided` now states that `<Exec>` and `<Atomic>` are supported top-level elements, but `handleStartElement` still returns an error claiming only `<Replace>` or `<Add>` are allowed when encountering an unsupported top-level element.
## Issue Context
This message is user-visible (surfaced via Windows profile upload API) and also used in tests.
## Fix Focus Areas
- server/fleet/windows_mdm.go[85-89]
- server/fleet/windows_mdm.go[167-175]
### Implementation notes
Pick one consistent approach:
- Update the error string to list all supported top-level elements (`Replace`, `Add`, `Exec`, `Atomic`), OR
- Revert/adjust the comment to match the intended user guidance if Design specifically wants to mention only Replace/Add.
Update tests accordingly if error text changes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR tightens validation of uploaded Windows MDM SyncML configuration profiles to prevent server-side acceptance of payloads that will later be rejected by Windows devices.

Changes:

  • Reject non-element XML inputs (e.g., plain text or comment-only) by requiring at least one supported SyncML top-level element (Replace, Add, Exec, Atomic).
  • Add LocURI format validation to require a ./ prefix and disallow ../ path traversal sequences.
  • Update and extend unit tests, and add two changelog entries describing the validation fixes.

Reviewed changes

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

File Description
server/fleet/windows_mdm.go Adds SyncML “has supported top-level element” check and new LocURI format validation helper.
server/fleet/windows_mdm_test.go Updates existing fixtures for ./-prefixed LocURIs and adds new regression tests for the reported issues.
changes/42224-windows-locuri-format-validation Changelog entry for LocURI validation.
changes/42219-windows-profile-syncml-validation Changelog entry for SyncML top-level element validation.

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

Comment thread server/fleet/windows_mdm.go Outdated
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR requires uploaded Windows MDM profiles to include at least one supported SyncML top-level element (, , , or ) unless the Fleet secret placeholder is present. It enforces LocURI rules during parsing: an empty is rejected; values starting with '/' are rejected (unless they start with './'); and any path segment containing '..' is rejected. Tests and changelog entries were added and fixtures adjusted to reflect the new validation.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Windows MDM validation fixes' is concise and directly reflects the main objective of adding validation for Windows MDM configuration profiles.
Description check ✅ Passed The PR description follows the template with related issue references and most required checklist items checked; however, database migrations and fleetd compatibility sections were not addressed.
Linked Issues check ✅ Passed The code changes fully address both linked issues: #42219 (rejecting non-XML profiles without supported SyncML elements) and #42224 (validating LocURI format, rejecting empty/leading-slash values and path traversal).
Out of Scope Changes check ✅ Passed All code changes are directly scoped to the linked issues: changelog entries for both issues and validation logic for SyncML elements and LocURI format in windows_mdm.go with corresponding test updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch victor/42219-42224-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@server/fleet/windows_mdm.go`:
- Around line 141-143: The error string returned when v.sawValidTopLevel is
false does not match the required `#42219` contract; update the return value in
the branch that checks v.sawValidTopLevel (the code that currently returns
errors.New(...)) to exactly "The file should include valid SyncML XML with at
least one supported element." so the API/UI error text matches the spec.
- Around line 240-257: The current validateLocURIFormat function accepts any
path beginning with "./"; tighten it to only allow the OMA-DM roots by verifying
that trimmed strings either are empty or do not contain "../" and start with one
of the explicit prefixes "./Device/", "./User/", or "./Vendor/". Update
validateLocURIFormat to first trim and return nil for empty, keep the "../"
check, then replace the generic HasPrefix("./") check with a check that the
value HasPrefix one of the three allowed prefixes and return a specific error if
it doesn't match those roots.
🪄 Autofix (Beta)

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: CHILL

Plan: Pro

Run ID: 0f480363-b7dc-42ad-80db-6d141a0de392

📥 Commits

Reviewing files that changed from the base of the PR and between f9c9ad0 and 0ea802c.

📒 Files selected for processing (4)
  • changes/42219-windows-profile-syncml-validation
  • changes/42224-windows-locuri-format-validation
  • server/fleet/windows_mdm.go
  • server/fleet/windows_mdm_test.go

Comment thread server/fleet/windows_mdm.go Outdated
Comment thread server/fleet/windows_mdm.go Outdated
@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.82%. Comparing base (f9c9ad0) to head (1ca1a4e).
⚠️ Report is 30 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #46029   +/-   ##
=======================================
  Coverage   66.82%   66.82%           
=======================================
  Files        2754     2754           
  Lines      220137   220162   +25     
  Branches    11025    11025           
=======================================
+ Hits       147114   147134   +20     
- Misses      59727    59732    +5     
  Partials    13296    13296           
Flag Coverage Δ
backend 68.63% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@getvictor getvictor marked this pull request as ready for review May 22, 2026 19:01
@getvictor getvictor requested a review from a team as a code owner May 22, 2026 19:01

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
server/fleet/windows_mdm.go (1)

91-93: ⚡ Quick win

ValidateUserProvided doc comment no longer matches LocURI behavior.

The comment says LocURI “must start with ./”, but validateLocURIFormat and tests explicitly allow forms like Device/Vendor/.... Please align the comment with the implemented rule to avoid contract confusion.

🤖 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 `@server/fleet/windows_mdm.go` around lines 91 - 93, Update the
ValidateUserProvided doc comment in windows_mdm.go to match the implemented
LocURI rules in validateLocURIFormat: remove the blanket "must start with './'"
statement and state that LocURI may either be a relative OMA-DM path beginning
with "./" or a device-style path like "Device/..." (and must not contain "../"
traversal), and also note that LocURI values within <Target> must not be
reserved Fleet-managed URIs; reference validateLocURIFormat and
ValidateUserProvided so readers know where the enforced rules live.
🤖 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 `@server/fleet/windows_mdm.go`:
- Around line 114-117: The current check uses bytes.Contains(m.SyncML,
[]byte(ServerSecretPrefix)) which is too permissive; change
validator.containsServerSecret to only match the secret placeholder when it
appears with the expected sigil (e.g. b"$"+ServerSecretPrefix) or otherwise
validate the surrounding SyncML token boundary (so plain text "FLEET_SECRET_"
doesn't bypass checks). In practice replace the bytes.Contains call with a
scoped check that searches for []byte("$"+ServerSecretPrefix) or uses a
regex/token parse against m.SyncML to ensure the "$" sigil precedes
ServerSecretPrefix; apply the same stricter check wherever the original
contains-based test appears (e.g. the other occurrence handling
supported-top-level-element verification).

---

Nitpick comments:
In `@server/fleet/windows_mdm.go`:
- Around line 91-93: Update the ValidateUserProvided doc comment in
windows_mdm.go to match the implemented LocURI rules in validateLocURIFormat:
remove the blanket "must start with './'" statement and state that LocURI may
either be a relative OMA-DM path beginning with "./" or a device-style path like
"Device/..." (and must not contain "../" traversal), and also note that LocURI
values within <Target> must not be reserved Fleet-managed URIs; reference
validateLocURIFormat and ValidateUserProvided so readers know where the enforced
rules live.
🪄 Autofix (Beta)

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: CHILL

Plan: Pro

Run ID: 15fe56c5-b58f-497a-ae4b-4d222df99774

📥 Commits

Reviewing files that changed from the base of the PR and between 0ea802c and 1ca1a4e.

📒 Files selected for processing (4)
  • changes/42219-windows-profile-syncml-validation
  • changes/42224-windows-locuri-format-validation
  • server/fleet/windows_mdm.go
  • server/fleet/windows_mdm_test.go
✅ Files skipped from review due to trivial changes (2)
  • changes/42219-windows-profile-syncml-validation
  • changes/42224-windows-locuri-format-validation

Comment on lines +114 to +117
// Substring match for the secret prefix. A literal "FLEET_SECRET_" appearing in profile data with no "$" sigil would
// also flip this flag, but the only consequence is skipping the top-level element check on that upload, which is
// acceptable.
validator.containsServerSecret = bytes.Contains(m.SyncML, []byte(ServerSecretPrefix))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Secret-placeholder bypass is over-broad and can accept unintended non-SyncML payloads.

Using bytes.Contains(m.SyncML, []byte(ServerSecretPrefix)) means any payload containing FLEET_SECRET_ (including plain text/comments) skips the supported-top-level-element check. That weakens the new #42219 protection and can admit invalid uploads.

Proposed fix
-	validator.containsServerSecret = bytes.Contains(m.SyncML, []byte(ServerSecretPrefix))
+	validator.containsServerSecret =
+		bytes.Contains(m.SyncML, []byte("$"+ServerSecretPrefix)) ||
+		bytes.Contains(m.SyncML, []byte("${"+ServerSecretPrefix))

Also applies to: 149-153

🤖 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 `@server/fleet/windows_mdm.go` around lines 114 - 117, The current check uses
bytes.Contains(m.SyncML, []byte(ServerSecretPrefix)) which is too permissive;
change validator.containsServerSecret to only match the secret placeholder when
it appears with the expected sigil (e.g. b"$"+ServerSecretPrefix) or otherwise
validate the surrounding SyncML token boundary (so plain text "FLEET_SECRET_"
doesn't bypass checks). In practice replace the bytes.Contains call with a
scoped check that searches for []byte("$"+ServerSecretPrefix) or uses a
regex/token parse against m.SyncML to ensure the "$" sigil precedes
ServerSecretPrefix; apply the same stricter check wherever the original
contains-based test appears (e.g. the other occurrence handling
supported-top-level-element verification).

@getvictor getvictor merged commit 4146978 into main May 24, 2026
42 checks passed
@getvictor getvictor deleted the victor/42219-42224-validation branch May 24, 2026 13:02
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.

Windows MDM LocURI with leading / or empty accepted (no validation) Non-XML content accepted as valid Windows MDM profile

3 participants