Skip to content

iOS/iPadOS managed config: validator (#43963) - #44930

Merged
cdcme merged 7 commits into
mainfrom
iosmac/43963-validation
May 12, 2026
Merged

iOS/iPadOS managed config: validator (#43963)#44930
cdcme merged 7 commits into
mainfrom
iosmac/43963-validation

Conversation

@cdcme

@cdcme cdcme commented May 7, 2026

Copy link
Copy Markdown
Member

Part of #38790 (iOS / iPadOS managed app configuration).

Closes #43963.

Adds ValidateAppleAppConfiguration and the FleetVarsSupportedInAppleAppConfig allow-list in server/fleet/vpp.go. Walks the decoded plist (keys + string values) so XML-entity-encoded $FLEET_VAR_* tokens can't slip past the disallow check, and rejects non-XML plist formats (binary, OpenStep, GNUStep) since Apple's InstallApplication only accepts XML.

Stacked PRs (review bottom up):

Summary by CodeRabbit

  • New Features

    • Added validation for Apple managed app configurations to allow only supported Fleet variable placeholders, reject malformed plist formats, and accept empty payloads.
  • Bug Fixes

    • Improved handling of app configuration payloads to ensure consistent validation and error responses across Android and iOS flows.
  • Tests

    • Added comprehensive tests covering plist validation, allowed/disallowed variables, and edge cases to increase reliability.

Review Change Stack

jkatz01 and others added 6 commits May 4, 2026 14:42
…o []byte

Replaces the stub from the datastore-methods branch (#43964) with a real
plist validator. Lives in server/fleet/vpp.go alongside the rest of VPP
because validation is part of the InstallApplication command flow.

- ValidateAppleAppConfiguration parses the payload via howett.net/plist
  into a map[string]any (which naturally rejects non-dict roots), then
  walks string-typed leaves checking Fleet variable tokens against the
  app-config allow-list.
- FleetVarsSupportedInAppleAppConfig: host-scoped subset of the variables
  permitted in Apple configuration profiles. Excludes credential / SCEP /
  NDES variables that don't fit the InstallApplication shape.
- Configuration field type: json.RawMessage -> []byte across VPPAppTeam,
  VPPAppStoreApp, AppStoreAppUpdatePayload. The Apple payload is plist
  XML, not JSON, so the field is just opaque bytes. Existing Android
  callers pass and receive []byte transparently (json.RawMessage is
  []byte-underlying, assignable in both directions).
- Updated comments on the three Configuration fields to reflect Apple
  support.
- No size cap in the validator — caps belong at the API layer.
- Empty input is allowed: callers (e.g. the gitops change-detector
  in vpp.go) decide whether to store or clear.

Issue: #43963
Replaces the ad-hoc walker over the parsed plist tree with a raw-bytes scan
using server/variables.Find — the same helper validateConfigProfileFleetVariables
in apple_mdm.go uses for profile validation. Drops the local
fleetVarTokenRegexp, the per-call allowed-set map, the walkAppleAppConfigStrings
recursive walker, and the higher-order-function callback style.

The plist.Unmarshal step stays as the structural check (must be valid plist
with a <dict> root). Token allow-list check is now a flat loop with
slices.Contains.
@codecov

codecov Bot commented May 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.81%. Comparing base (4be56c1) to head (2ed642d).
⚠️ Report is 251 commits behind head on main.

Files with missing lines Patch % Lines
server/fleet/vpp.go 90.00% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #44930      +/-   ##
==========================================
+ Coverage   66.69%   66.81%   +0.12%     
==========================================
  Files        2651     2665      +14     
  Lines      213559   216451    +2892     
  Branches     9647     9647              
==========================================
+ Hits       142424   144621    +2197     
- Misses      58169    58667     +498     
- Partials    12966    13163     +197     
Flag Coverage Δ
backend 68.67% <90.00%> (+0.11%) ⬆️

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.

@cdcme

cdcme commented May 11, 2026

Copy link
Copy Markdown
Member Author

@claude review once

@cdcme
cdcme marked this pull request as ready for review May 11, 2026 12:45
Copilot AI review requested due to automatic review settings May 11, 2026 12:45

@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.

@cdcme
cdcme requested a review from a team as a code owner May 11, 2026 12:45

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

Adds backend validation support for iOS/iPadOS “managed app configuration” payloads (XML plist) and introduces an allow-list of supported $FLEET_VAR_* tokens for use in Apple app configurations.

Changes:

  • Added ValidateAppleAppConfiguration to parse/validate XML plist <dict> payloads and reject unsupported Fleet variables (including entity-encoded tokens).
  • Introduced FleetVarsSupportedInAppleAppConfig allow-list for variables permitted in iOS/iPadOS managed app configuration.
  • Updated several tests and VPP structs to use []byte for Configuration.

Reviewed changes

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

Show a summary per file
File Description
server/service/integration_android_software_test.go Updates expectations around the configuration field type in integration tests.
server/fleet/vpp.go Adds Apple app configuration validation + allow-list; changes Configuration field types.
server/fleet/vpp_test.go Adds unit tests for ValidateAppleAppConfiguration.
server/datastore/mysql/vpp_test.go Adjusts datastore tests for the new Configuration field type.
server/datastore/mysql/android_test.go Adjusts Android configuration tests for the new Configuration field type.

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

Comment thread server/fleet/vpp.go
Comment on lines 132 to +137
// "Browsers", etc.
Categories []string `json:"categories"`
DisplayName string `json:"display_name"`
// Configuration is a json file used to customize Android app
// behavior/settings. Applicable to Android apps only.
Configuration json.RawMessage `json:"configuration,omitempty"`
// Configuration is the managed app configuration payload. JSON for Android,
// XML for iOS / iPadOS.
Configuration []byte `json:"configuration,omitempty"`
Comment thread server/fleet/vpp.go
Comment on lines +62 to +67
// Configuration is the managed app configuration payload. JSON for Android,
// XML for iOS / iPadOS.
Configuration []byte `json:"configuration,omitempty"`
AutoUpdateEnabled *bool `json:"-"`
AutoUpdateStartTime *string `json:"-"`
AutoUpdateEndTime *string `json:"-"`
Comment thread server/fleet/vpp.go
Comment thread server/fleet/vpp.go
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7331663e-b6d0-479a-8802-b4d48d4f79ce

📥 Commits

Reviewing files that changed from the base of the PR and between 1da4c58 and 2ed642d.

📒 Files selected for processing (2)
  • server/fleet/vpp.go
  • server/fleet/vpp_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/fleet/vpp_test.go
  • server/fleet/vpp.go

Walkthrough

This pull request changes VPP app configuration payload representation from json.RawMessage to []byte across three data structures (VPPAppTeam, VPPAppStoreApp, AppStoreAppUpdatePayload) and introduces validation for Apple iOS/iPadOS managed app configurations. The new ValidateAppleAppConfiguration function parses XML plist, enforces XML format, and rejects configurations containing unsupported $FLEET_VAR_* tokens outside an allow-list. All dependent test assertions and fixtures are updated to reflect the type change.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.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 title clearly identifies the main feature being added (validator for iOS/iPadOS managed config) and references the issue number, making it specific and descriptive.
Description check ✅ Passed The description covers the main changes, links related issues, mentions the stacked PRs, and explains the core functionality. However, it lacks some checklist items from the template (changes files, testing details, etc.).
Linked Issues check ✅ Passed The PR implements all key requirements from #43963: ValidateAppleAppConfiguration function with XML plist validation, FleetVarsSupportedInAppleAppConfig allow-list, rejection of non-XML formats, and comprehensive unit tests covering valid/invalid cases.
Out of Scope Changes check ✅ Passed Changes are properly scoped to implement the validator and update field types from json.RawMessage to []byte. Test file updates align with the type changes, and no unrelated modifications are present.

✏️ 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 iosmac/43963-validation

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

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

255-283: 💤 Low value

Helper is correct; consider a small UX nit on the "not a dict" error path.

findUnsupportedFleetVar correctly limits recursion to the three plist value kinds that can carry text (string, map[string]any, []any) — int64/float64/bool/[]byte/time.Time cannot legally contain a $FLEET_VAR_* reference, so skipping them is the right behavior.

One small UX consideration on the validator above: if a user submits an XML plist whose root is <array> (or any other non-dict), plist.Unmarshal(..., &root) will surface a generic "cannot unmarshal array into Go value of type map[string]interface {}" error to the caller. The function docstring explicitly promises that the root must be a <dict>, so a dedicated error message would read better. If you want to keep the strict-typing approach, you can sniff the root with any first and switch on the concrete type before unmarshaling into map[string]any. Not blocking — totally fine to keep as-is.

🤖 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/vpp.go` around lines 255 - 283, The validator currently
unmarshals plist directly into map[string]any and returns the raw "cannot
unmarshal array into Go value of type map[string]interface {}" error for
non-dict roots; change that code to first unmarshal into an any (interface{})
value, switch on its concrete type, and if the root is not map[string]any return
a clear, user-facing error like "plist root must be a <dict>, got <array>" (or
include the actual concrete type), otherwise cast/convert the value to
map[string]any and continue (so keep using findUnsupportedFleetVar and other
logic unchanged).
🤖 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/vpp.go`:
- Around line 62-67: The Configuration field was changed from json.RawMessage to
[]byte in VPPAppTeam, VPPAppStoreApp, and AppStoreAppUpdatePayload which causes
responses to be base64-encoded; either revert Configuration back to
json.RawMessage on those types or implement custom MarshalJSON/UnmarshalJSON on
each type (or a shared wrapper type) to emit/accept raw JSON/XML to preserve the
original wire shape; then ensure request types addAppStoreAppRequest,
updateAppStoreAppRequest, and VPPBatchPayload remain compatible and update API
docs and downstream consumers (UI, fleetctl/gitops, Terraform, REST clients) to
handle the chosen format.

---

Nitpick comments:
In `@server/fleet/vpp.go`:
- Around line 255-283: The validator currently unmarshals plist directly into
map[string]any and returns the raw "cannot unmarshal array into Go value of type
map[string]interface {}" error for non-dict roots; change that code to first
unmarshal into an any (interface{}) value, switch on its concrete type, and if
the root is not map[string]any return a clear, user-facing error like "plist
root must be a <dict>, got <array>" (or include the actual concrete type),
otherwise cast/convert the value to map[string]any and continue (so keep using
findUnsupportedFleetVar and other logic unchanged).
🪄 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: 08ab203f-d961-41d1-a9d6-886926032faf

📥 Commits

Reviewing files that changed from the base of the PR and between 292bab3 and 1da4c58.

📒 Files selected for processing (5)
  • server/datastore/mysql/android_test.go
  • server/datastore/mysql/vpp_test.go
  • server/fleet/vpp.go
  • server/fleet/vpp_test.go
  • server/service/integration_android_software_test.go

Comment thread server/fleet/vpp.go
Comment on lines +62 to +67
// Configuration is the managed app configuration payload. JSON for Android,
// XML for iOS / iPadOS.
Configuration []byte `json:"configuration,omitempty"`
AutoUpdateEnabled *bool `json:"-"`
AutoUpdateStartTime *string `json:"-"`
AutoUpdateEndTime *string `json:"-"`

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 | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1) Find every place that reads/writes app_store_app.configuration in non-Go code (UI, gitops, terraform, docs).
fd -t f -e ts -e tsx -e js -e jsx -e py -e go -e md -e yml -e yaml | xargs rg -n --no-messages 'app_store_app[^a-zA-Z0-9_]*\.?configuration|"configuration"\s*:'  | rg -v '_test\.go|integration_.*_test\.go' | head -200

# 2) Confirm the request-side types are still json.RawMessage (asymmetry check).
rg -nP --type=go -C2 '\b(addAppStoreAppRequest|updateAppStoreAppRequest|VPPBatchPayload)\b' | rg -nP -C2 'Configuration'

# 3) See whether any place attaches a custom (Un)MarshalJSON to VPPAppStoreApp or VPPAppTeam to neutralize the wire-format change.
ast-grep --pattern $'func ($_ $_VPPAppStoreApp) MarshalJSON() ($_, $_) { $$$ }'
ast-grep --pattern $'func ($_ $_VPPAppTeam) MarshalJSON() ($_, $_) { $$$ }'
ast-grep --pattern $'func ($_ $_AppStoreAppUpdatePayload) MarshalJSON() ($_, $_) { $$$ }'

Repository: fleetdm/fleet

Length of output: 9918


🏁 Script executed:

# Get the current state of VPPAppStoreApp and related structures
sed -n '50,80p' server/fleet/vpp.go | cat -n

Repository: fleetdm/fleet

Length of output: 1535


🏁 Script executed:

# Get more context around the mentioned lines
sed -n '55,75p' server/fleet/vpp.go | cat -n

Repository: fleetdm/fleet

Length of output: 1028


🏁 Script executed:

# Search for the request-side type definitions
rg -n 'type (addAppStoreAppRequest|updateAppStoreAppRequest|VPPBatchPayload)' --type=go -A 15

Repository: fleetdm/fleet

Length of output: 6038


🏁 Script executed:

# Look for marshal implementations on the response types
rg -n 'func.*VPPAppStoreApp.*Marshal' --type=go

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Check the specific test line 419 mentioned in the comment
sed -n '410,430p' server/service/integration_android_software_test.go | cat -n

Repository: fleetdm/fleet

Length of output: 995


🏁 Script executed:

# Check the other affected lines mentioned (135-137, 196-205)
sed -n '130,210p' server/fleet/vpp.go | cat -n

Repository: fleetdm/fleet

Length of output: 4022


🏁 Script executed:

# Search for any related API documentation or changelog
fd -i -name '*.md' | xargs rg -l 'app.store|vpp|configuration' | head -10

Repository: fleetdm/fleet

Length of output: 623


🏁 Script executed:

# Check if there are any changelog or migration notes
find . -type f \( -name 'CHANGELOG*' -o -name 'changelog*' -o -name 'MIGRATION*' \) | head -5

Repository: fleetdm/fleet

Length of output: 214


🏁 Script executed:

# Look for TypeScript/JavaScript client code that might handle this
fd -type f \( -e ts -e tsx -e js -e jsx \) | xargs rg -l 'configuration' | grep -i client | head -10

Repository: fleetdm/fleet

Length of output: 589


🏁 Script executed:

# Look for recent changes to vpp.go to understand the intent
git log --oneline -20 -- server/fleet/vpp.go 2>/dev/null || echo "Git history not available"

Repository: fleetdm/fleet

Length of output: 95


🏁 Script executed:

# Search for any TODOs or FIXMEs related to Configuration
rg -n 'TODO|FIXME' server/fleet/vpp.go server/service/vpp.go

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Look for any frontend/TypeScript references to configuration in app store app context
rg -n 'appStoreApp|app_store_app' --type=ts --type=tsx --type=js --type=jsx | head -20

Repository: fleetdm/fleet

Length of output: 85


🏁 Script executed:

# Check if there are any API integration tests that validate the wire format
rg -n 'Configuration.*base64|base64.*Configuration' --type=go

Repository: fleetdm/fleet

Length of output: 513


🏁 Script executed:

# Look for JSON marshaling tests
rg -n 'json.Marshal.*Configuration|Configuration.*json.Marshal' --type=go | head -10

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Look at how Configuration is handled in responses - find the handler that returns it
rg -n 'getSoftwareTitle|software/titles' server/service/vpp.go -A 10 | head -40

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Check if there's any special handling when retrieving Configuration from DB
rg -n 'Configuration' server/datastore/mysql/vpp.go -B 2 -A 2 | head -50

Repository: fleetdm/fleet

Length of output: 1959


🏁 Script executed:

# Look for how the response is constructed
rg -n 'type.*Response.*struct' server/service/vpp.go -A 15 | grep -A 15 'getSoftwareTitleResponse\|getSoftwareTitle'

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Check how configuration is serialized when storing/retrieving
rg -n 'db:.*configuration|json:.*configuration' server/fleet/vpp.go --ignore-case

Repository: fleetdm/fleet

Length of output: 181


🏁 Script executed:

# Check git blame for when Configuration changed from json.RawMessage to []byte
git log -p --all -- server/fleet/vpp.go | grep -B 20 -A 5 'Configuration.*\[\]byte' | head -60

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Look for PR/commit messages mentioning this change
git log --all --grep='Configuration\|json.RawMessage\|\[\]byte' --oneline | head -20

Repository: fleetdm/fleet

Length of output: 1341


🏁 Script executed:

# Check if there's documentation about the wire format
fd -type f -name '*.md' | xargs rg -l 'software.*title\|app.*store' | head -5

Repository: fleetdm/fleet

Length of output: 228


🏁 Script executed:

# Search for any UI code consuming the configuration endpoint
find . -type f \( -name '*.ts' -o -name '*.js' \) -path '*/frontend/*' -o -path '*/web/*' | xargs rg -l 'configuration' 2>/dev/null | head -10

Repository: fleetdm/fleet

Length of output: 442


🏁 Script executed:

# Check the frontend software interface to see how configuration is handled
cat frontend/interfaces/software.ts | head -150

Repository: fleetdm/fleet

Length of output: 4564


🏁 Script executed:

# Look for any handling of app_store_app in the frontend
rg -n 'app_store_app\|appStoreApp' frontend/ | head -30

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Check if there's any base64 encoding/decoding in the frontend
rg -n 'btoa\|atob\|base64' frontend/services/entities/software.ts | head -20

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Look at the actual git commit that changed the type
git log --all -p -- server/fleet/vpp.go | grep -B 30 -A 5 'Configuration.*\[\]byte.*json' | head -80

Repository: fleetdm/fleet

Length of output: 4311


🏁 Script executed:

# Try to find when this change was introduced
git log --all --pretty=format:"%H %s" -- server/fleet/vpp.go | head -30

Repository: fleetdm/fleet

Length of output: 534


🏁 Script executed:

# Look at the specific commit that changed Configuration to []byte
git show 1da4c58d --stat

Repository: fleetdm/fleet

Length of output: 50370


🏁 Script executed:

# Get the full commit message
git show 1da4c58d --no-patch --format=fuller

Repository: fleetdm/fleet

Length of output: 317


🏁 Script executed:

# Check if there are any issues or PRs related to this
rg -n 'plist\|xml.*format\|base64' server/fleet/vpp.go server/service/vpp.go | head -20

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Look at the actual implementation of how Configuration is stored/retrieved
sed -n '100,150p' server/datastore/mysql/vpp.go | cat -n

Repository: fleetdm/fleet

Length of output: 1929


🏁 Script executed:

# Check if the test actually validates the wire format properly
sed -n '410,450p' server/service/integration_android_software_test.go | cat -n

Repository: fleetdm/fleet

Length of output: 2032


🏁 Script executed:

# Check what the commit actually changed - look at the diff for the relevant lines
git show 1da4c58d -- server/fleet/vpp.go | grep -A 5 -B 5 'Configuration'

Repository: fleetdm/fleet

Length of output: 2787


🏁 Script executed:

# Search for any issues or PRs referencing this change
rg -n 'base64|RawMessage.*byte|byte.*RawMessage' server/fleet/vpp.go server/service/vpp.go

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Check if there are tests that specifically validate JSON marshaling
rg -n 'json.Marshal.*Configuration\|MarshalJSON' server/service/ --type=go -B 2 -A 2 | head -40

Repository: fleetdm/fleet

Length of output: 39


🏁 Script executed:

# Look at the Android configuration storage to understand how it flows
rg -n 'GetAndroidAppConfiguration\|updateAndroidAppConfigurationTx' server/datastore/mysql/android* --type=go -A 10 | head -60

Repository: fleetdm/fleet

Length of output: 39


Confirm this wire format change is intentional and verify downstream consumers have been updated.

Configuration was changed from json.RawMessage to []byte in VPPAppTeam, VPPAppStoreApp, and AppStoreAppUpdatePayload response types. This introduces a breaking wire format change:

  • Requests still accept raw JSON via json.RawMessage in addAppStoreAppRequest, updateAppStoreAppRequest, and VPPBatchPayload
  • Responses now emit base64-encoded bytes instead of raw JSON/XML, since encoding/json base64-encodes []byte fields
  • No custom MarshalJSON/UnmarshalJSON implementations exist to preserve the previous wire shape

Go integration tests pass because Go's decoder accepts both formats transparently. However, non-Go consumers (UI, fleetctl/gitops, Terraform, REST clients) will receive base64-encoded configuration instead of the expected raw JSON/XML structure, requiring code changes.

Ensure this change is intentional and that API documentation, UI code, and any clients consuming the software-titles endpoints have been updated to handle the new base64-encoded format. If backward compatibility is required, implement custom marshal/unmarshal methods to preserve the original wire format.

🤖 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/vpp.go` around lines 62 - 67, The Configuration field was
changed from json.RawMessage to []byte in VPPAppTeam, VPPAppStoreApp, and
AppStoreAppUpdatePayload which causes responses to be base64-encoded; either
revert Configuration back to json.RawMessage on those types or implement custom
MarshalJSON/UnmarshalJSON on each type (or a shared wrapper type) to emit/accept
raw JSON/XML to preserve the original wire shape; then ensure request types
addAppStoreAppRequest, updateAppStoreAppRequest, and VPPBatchPayload remain
compatible and update API docs and downstream consumers (UI, fleetctl/gitops,
Terraform, REST clients) to handle the chosen format.

@jkatz01 jkatz01 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for improving this! The validation looks pretty thorough.

@cdcme
cdcme merged commit 3517ff6 into main May 12, 2026
61 of 62 checks passed
@cdcme
cdcme deleted the iosmac/43963-validation branch May 12, 2026 17:23
cdcme added a commit that referenced this pull request May 12, 2026
Part of #38790. Stacked on top of #44930.

Closes #43964.

Adds VPP and in-house datastore methods (`GetVPPAppConfiguration`,
`BulkGetVPPAppConfigurations`, `DeleteVPPAppConfiguration`,
`HasVPPAppConfigurationChanged`, plus in-house equivalents) keyed on the
merged `vpp_app_configurations` and `in_house_app_configurations`
tables. Wires them into `InsertVPPAppWithTeam`, `SaveInHouseAppUpdates`,
and the team / app removal paths.

Two follow-up bug fixes folded into this branch:
1. iPadOS in-house apps received no configuration: a single `.ipa`
upload creates two `in_house_apps` rows but config was only stored
against the iOS row's id, so iPadOS lookups returned NotFound. Now
writes to both sibling rows on insert and propagates updates / clears
via `installerIDsForInHouseAppSibling`.
2. Single-app PATCH with `"configuration": null` was inserting empty
bytes instead of deleting; aligned the iOS/iPadOS branch in
`InsertVPPAppWithTeam` with the batch path's `len > 0` upsert / `len ==
0` delete semantics.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added support for managing in-house app configurations including
storage, updates, and deletion.
* Expanded VPP app configuration support for iOS/iPadOS platforms with
configuration storage and change detection.

* **Refactor**
* Updated Android app configuration handling to improve internal data
consistency and streamline configuration management workflows across all
app types.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/44931)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: jkatz01 <yehonatankatz@gmail.com>
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.

IOSMAC: Backend types + validation for Apple managed app config

3 participants