feat(devicepolicy): enforce VS Code extension allowlist#138
Draft
raysubham wants to merge 5 commits into
Draft
Conversation
Add the developer-MDM policy enforcement loop for the ide_extension category. The agent stays thin: the backend compiles the effective policy; the agent writes it to the OS-native VS Code policy location, verifies it, and reports compliance — VS Code itself does the disabling. - fetch: GET /v1/:customer/developer-mdm-agent/devices/:device_id/ effective-policy?category=ide_extension; backend hash persisted and echoed verbatim (never recomputed); non-object policy rejected - writers: Windows registry (REG_SZ AllowedExtensions under the VS Code policies key) and Linux /etc/vscode/policy.json; only the AllowedExtensions value is managed, coexisting policies preserved; macOS/other get no writer (MDM-export path) - reconcile: value-based ownership — clear/overwrite only when the on-disk value equals the recorded written value, any other present value yields mdm_managed; ownership-store writability preflight before any policy write, rollback to pre-cycle disk state if the post-write persist fails; ownership recorded on every successful write so a transient readback mismatch self-corrects next cycle; idempotent re-apply keyed on backend hash - verify/report: compliant | policy_not_applied | vscode_unsupported | mdm_managed | write_failed | verification_failed; applied_hash only when readback-confirmed; clear path reports nothing - main: runIDEExtensionEnforce on the existing scheduled cycle, gated behind FeatureDevMDMPolicies (off until GA; override for dogfooding) - CI: windows-latest job runs the windows-tagged writer tests natively (the macOS test job can only cross-compile them)
…policy locations Rework the enforcement surface (plan rev 2026-06-11): the agent now converges the `extensions.allowed` key in the user's VS Code settings.json — one writer for Windows, macOS, and Linux — instead of writing OS managed-policy locations. macOS moves from export-only to agent-enforced; real MDM-managed devices are detected and yielded to. - settings writer: format-preserving single-key JSONC merge via tailscale/hujson (RFC 6902 patch) — comments, trailing commas, and every other key survive byte-for-byte; atomic temp+fsync+rename with capped sibling backups (internal/aiagents/atomicfile); an unparseable settings.json is never rewritten; the file is never deleted, Clear removes only the key; per-OS path resolution (%APPDATA% / ~/Library/Application Support / $XDG_CONFIG_HOME) - managed-policy probe: read-only AllowedExtensions presence check at the OS policy location (HKLM registry / /etc/vscode/policy.json / /Library/Managed Preferences plists, machine-wide or per-user) → mdm_managed, skip write — a real policy outranks user settings - ladder rework: fetch → clear-if-owned → probe → idempotency → ownership-store preflight → drift detection (on-disk diverged from recorded written value → re-apply + drift_detected) → merge-write + readback → persist-every-write with rollback → verify → report; values compared in compacted canonical form - drop the VS Code version floor end-to-end: min_vscode_version gone from the fetch contract (backend removed it), version inputs gone from Verify, vscode_unsupported replaced by drift_detected in the reportable states, IDE version detection unwired from main - delete the HKLM/policy.json writers; retarget the windows CI job at the settings writer + registry probe
…files # Conflicts: # internal/featuregate/featuregate.go
"devicepolicy" matches the backend's vocabulary (device_id, registered devices, per-device compliance) and names what the package manages — the centrally assigned policy for this device — rather than borrowing the product label. The product/feature name stays "Developer MDM" where it refers to the product: the feature gate (FeatureDevMDMPolicies, "developer-mdm-policies"), the developer-mdm-agent auth channel, wire paths, and the backend sync references are all unchanged. Mechanical sweep, no behavior change: - internal/devmdm -> internal/devicepolicy (package clause + doc refs) - ownership state file: developer-mdm-policy-state.json -> device-policy-state.json (pre-ship, nothing has written it yet) - cmd wiring: devMDMEnforceTimeout -> devicePolicyEnforceTimeout, AppendError source tag "devmdm" -> "devicepolicy" - CI: test-windows-devmdm job -> test-windows-devicepolicy - windows probe test key: ...\DevMDMProbe -> ...\DevicePolicyProbe
…nto settings_writer.go Pure file consolidation, no behavior or API change: - fetch.go + report.go -> api.go (and their tests -> api_test.go): the Fetcher and Reporter are the two halves of the same developer-mdm-agent HTTP channel and share config gating, auth, timeout, and error style. - writer.go -> settings_writer.go: the Writer interface now lives beside its only production implementation. go doc -all diff before/after confirms the exported surface is identical apart from the reworded file references in comments.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the developer-MDM policy enforcement loop for the
ide_extensioncategory (Windows + Linux; macOS stays on the MDM-export path). The agent stays thin: the backend compiles the effective policy, the agent writes it to the OS-native VS Code managed-policy location, reads it back, and reports compliance — VS Code itself does the extension disabling.What's in here
Fetch (
internal/devmdm/fetch.go)GET /v1/:customer/developer-mdm-agent/devices/:device_id/effective-policy?category=ide_extensionon the existing agent auth channelhashis persisted and echoed verbatim in compliance reports — never recomputed — so the backend's byte-exactapplied == desiredcheck gatescompliantpolicy) are rejected; enforcement on disk is never wiped on a transient/malformed fetchWriters (
writer_windows.go/writer_linux.go/writer_file.go)AllowedExtensionsREG_SZ under the VS Code policies key; wrong-typed values surface as foreign (never overwritten)AllowedExtensionskey in/etc/vscode/policy.jsonextensions.allowedobject as a stringified-JSON string on every platform (the shape VS Code reads)Reconciler (
reconcile.go)mdm_managed, agent yieldsWiring + gating
runIDEExtensionEnforceruns on the existing scheduled cycle inmain.goFeatureDevMDMPolicies— off until GA (backendMinEnforcementAgentVersionis still a placeholder); enable via gate override for dogfoodingCI
test-windows-devmdmjob (windows-latest) intests.ymlruns the windows-tagged registry-writer tests natively — the macOS test job can only cross-compile them, so without this the production writer would ship with zero executed coverageTest plan
go test -race -count=1 ./...— full suite green (33 packages)go vetclean on darwin, linux, windows;gofmtclean;go mod tidyno driftPre-GA follow-ups (not in this PR)
MinEnforcementAgentVersionFeatureDevMDMPolicieson