diff --git a/cmd/fleetctl/mdm_test.go b/cmd/fleetctl/mdm_test.go
index 6002a7272a..7b19850376 100644
--- a/cmd/fleetctl/mdm_test.go
+++ b/cmd/fleetctl/mdm_test.go
@@ -205,7 +205,11 @@ func TestMDMRunCommand(t *testing.T) {
}
return hosts, nil
}
+ winCmds := map[string]struct{}{}
ds.MDMWindowsInsertCommandForHostsFunc = func(ctx context.Context, deviceIDs []string, cmd *fleet.MDMWindowsCommand) error {
+ // every command uuid is different
+ require.NotContains(t, winCmds, cmd.CommandUUID)
+ winCmds[cmd.CommandUUID] = struct{}{}
return nil
}
ds.GetMDMWindowsBitLockerStatusFunc = func(ctx context.Context, host *fleet.Host) (*fleet.HostMDMDiskEncryption, error) {
diff --git a/server/service/client_mdm.go b/server/service/client_mdm.go
index 7a50f548dc..3195106415 100644
--- a/server/service/client_mdm.go
+++ b/server/service/client_mdm.go
@@ -18,6 +18,7 @@ import (
"path/filepath"
"strings"
+ "github.com/beevik/etree"
"github.com/fleetdm/fleet/v4/pkg/file"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/google/uuid"
@@ -334,7 +335,23 @@ func (c *Client) prepareWindowsMDMCommand(rawCmd []byte) ([]byte, error) {
if _, err := fleet.ParseWindowsMDMCommand(rawCmd); err != nil {
return nil, err
}
- return rawCmd, nil
+
+ // ensure there's a CmdID with a random UUID value, we're manipulating
+ // the document this way to make sure we don't introduce any unintended
+ // changes to the command XML.
+ doc := etree.NewDocument()
+ if err := doc.ReadFromBytes(rawCmd); err != nil {
+ return nil, err
+ }
+ element := doc.FindElement("//CmdID")
+ // if we can't find a CmdID, just add one.
+ if element == nil {
+ root := doc.Root()
+ element = root.CreateElement("CmdID")
+ }
+ element.SetText(uuid.NewString())
+
+ return doc.WriteToBytes()
}
func (c *Client) prepareAppleMDMCommand(rawCmd []byte) ([]byte, error) {
diff --git a/server/service/client_mdm_test.go b/server/service/client_mdm_test.go
new file mode 100644
index 0000000000..748bf19e58
--- /dev/null
+++ b/server/service/client_mdm_test.go
@@ -0,0 +1,66 @@
+package service
+
+import (
+ "testing"
+
+ "github.com/beevik/etree"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestPrepareWindowsMDMCommand(t *testing.T) {
+ c := &Client{}
+
+ validXML := []byte(`
+
+ some-id
+
+
+ `)
+
+ invalidCmdXML := []byte(`
+
+ some-id
+
+
+ `)
+
+ noCmdIDXML := []byte(`
+
+
+
+ `)
+
+ t.Run("Modifies valid CmdID", func(t *testing.T) {
+ modified, err := c.prepareWindowsMDMCommand(validXML)
+ assert.Nil(t, err)
+
+ doc := etree.NewDocument()
+ err = doc.ReadFromBytes(modified)
+ assert.Nil(t, err)
+
+ element := doc.FindElement("//CmdID")
+ assert.NotNil(t, element)
+ assert.NotEmpty(t, element.Text())
+ })
+
+ t.Run("Adds CmdID if missing", func(t *testing.T) {
+ modified, err := c.prepareWindowsMDMCommand(noCmdIDXML)
+ assert.Nil(t, err)
+
+ doc := etree.NewDocument()
+ err = doc.ReadFromBytes(modified)
+ assert.Nil(t, err)
+
+ element := doc.FindElement("//CmdID")
+ assert.NotNil(t, element)
+ assert.NotEmpty(t, element.Text())
+ })
+
+ t.Run("Returns error on invalid XML", func(t *testing.T) {
+ _, err := c.prepareWindowsMDMCommand(invalidCmdXML)
+ assert.NotNil(t, err)
+
+ _, err = c.prepareWindowsMDMCommand([]byte("