Skip to content

[pkg firewall] add the VS Code product.json patch lifecycle - #26

Open
dekkagaijin wants to merge 1 commit into
vscode-fw-2-json-primitivesfrom
vscode-fw-3-lifecycle
Open

[pkg firewall] add the VS Code product.json patch lifecycle#26
dekkagaijin wants to merge 1 commit into
vscode-fw-2-json-primitivesfrom
vscode-fw-3-lifecycle

Conversation

@dekkagaijin

Copy link
Copy Markdown
Contributor

Merge after #25

Second code chunk. Builds on the JSON primitives to give discovery, a three-state machine, and patch/restore. Still nothing calls it — the generator wiring is a later change — so this can be reviewed as the state machine it is.

vscode_install_paths stable and Insiders, macOS/Linux
vscode_edition_label from nameLong, so output names the edition
vscode_is_readonly_install snap/flatpak by path prefix
vscode_can_write probes without modifying
vscode_node_bin VS Code's own Electron, via CFBundleExecutable
vscode_managed_state unmanaged | current | stale
vscode_marker_field reads the marker, single-line or pretty-printed
vscode_patch / vscode_unpatch
vscode_patch_via_node / vscode_unpatch_via_node

The patch is one key set and one key removed:

serviceUrl -> the firewall's gallery URL. VS Code derives both
${serviceUrl}/extensionquery and
${serviceUrl}/vscode/{publisher}/{name}/latest from it.
extensionUrlTemplate -> removed. It is the fallback when the resource API
returns 5xx. Left in place, a firewall outage
silently resolves versions from www.vscode-unpkg.net,
bypassing the firewall exactly when it is unhealthy.
Removed, that failure retries our own extensionquery
and fails the install if that also fails.

controlUrl (Microsoft's malicious-extension revocation list), resourceUrlTemplate, itemUrl, publisherUrl, nlsBaseUrl, mcpUrl and accessSKUs are left alone, and the merge is key-level, so keys future VS Code versions add survive untouched.

Three states, because credentials rotate:

unmanaged marker absent -> capture the original, then patch
current marker + expected line -> no write at all
stale marker but not current -> restore first, then patch

Never patching on top of a patch is the invariant that lets credentials rotate indefinitely without the captured original drifting.

Failure modes are loud rather than silent, because a silent no-op is indistinguishable from success in an MDM log:

  • An unwritable product.json on macOS almost always means the missing App Management (TCC) grant, and says so. Root is not exempt from that gate.
  • snap/flatpak installs are refused by path prefix, not by [[ -w ]] — under root that test returns true on a read-only mount.
  • Not line-oriented (repackaged, minified) falls back to VS Code's own bundled Electron as node. Never hardcodes "Electron": stable ships "Code".
  • --dry-run redacts the _ak token. A deliberate deviation from the other ecosystems, which echo full credentialed URLs: this one is a bearer credential in a URL path, and MDM consoles retain output for more people than can read product.json.

Tests: 60 assertions covering the full state machine, both writer paths, the EPERM/TCC message, the snap refusal, dry-run redaction, and byte-exact restore after two patch cycles. Also patches and restores whichever real product.json is installed on the machine, asserting nothing version-specific.

https://endorlabs.atlassian.net/browse/LM-452

@dekkagaijin
dekkagaijin requested a review from shrey-endor August 4, 2026 23:06
@dekkagaijin dekkagaijin changed the title package-firewall: add the VS Code product.json patch lifecycle [pkg firewall] add the VS Code product.json patch lifecycle Aug 4, 2026
Second code chunk. Builds on the JSON primitives to give discovery, a
three-state machine, and patch/restore. Still nothing calls it — the generator
wiring is a later change — so this can be reviewed as the state machine it is.

  vscode_install_paths       stable and Insiders, macOS/Linux
  vscode_edition_label       from nameLong, so output names the edition
  vscode_is_readonly_install snap/flatpak by path prefix
  vscode_can_write           probes without modifying
  vscode_node_bin            VS Code's own Electron, via CFBundleExecutable
  vscode_managed_state       unmanaged | current | stale
  vscode_marker_field        reads the marker, single-line or pretty-printed
  vscode_patch / vscode_unpatch
  vscode_patch_via_node / vscode_unpatch_via_node

The patch is one key set and one key removed:

  serviceUrl            -> the firewall's gallery URL. VS Code derives both
                           ${serviceUrl}/extensionquery and
                           ${serviceUrl}/vscode/{publisher}/{name}/latest from it.
  extensionUrlTemplate  -> removed. It is the fallback when the resource API
                           returns 5xx. Left in place, a firewall outage
                           silently resolves versions from www.vscode-unpkg.net,
                           bypassing the firewall exactly when it is unhealthy.
                           Removed, that failure retries our own extensionquery
                           and fails the install if that also fails.

controlUrl (Microsoft's malicious-extension revocation list), resourceUrlTemplate,
itemUrl, publisherUrl, nlsBaseUrl, mcpUrl and accessSKUs are left alone, and the
merge is key-level, so keys future VS Code versions add survive untouched.

Three states, because credentials rotate:

  unmanaged  marker absent          -> capture the original, then patch
  current    marker + expected line -> no write at all
  stale      marker but not current -> restore first, *then* patch

Never patching on top of a patch is the invariant that lets credentials rotate
indefinitely without the captured original drifting.

Failure modes are loud rather than silent, because a silent no-op is
indistinguishable from success in an MDM log:

  - An unwritable product.json on macOS almost always means the missing App
    Management (TCC) grant, and says so. Root is not exempt from that gate.
  - snap/flatpak installs are refused by path prefix, not by [[ -w ]] — under
    root that test returns true on a read-only mount.
  - Not line-oriented (repackaged, minified) falls back to VS Code's own bundled
    Electron as node. Never hardcodes "Electron": stable ships "Code".
  - --dry-run redacts the _ak token. A deliberate deviation from the other
    ecosystems, which echo full credentialed URLs: this one is a bearer
    credential in a URL path, and MDM consoles retain output for more people
    than can read product.json.

Tests: 60 assertions covering the full state machine, both writer paths, the
EPERM/TCC message, the snap refusal, dry-run redaction, and byte-exact restore
after two patch cycles. Also patches and restores whichever real product.json is
installed on the machine, asserting nothing version-specific.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dekkagaijin
dekkagaijin force-pushed the vscode-fw-3-lifecycle branch from 1124b90 to 1f2193e Compare August 6, 2026 16:10
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.

1 participant