ci: Route npm package restore through the Central Feed Service - #1892
Merged
Conversation
SFI Network Isolation requires build pipelines to stop restoring packages directly from public feeds. These pipelines resolve everything from registry.npmjs.org today, so they cannot run on a network isolated pool and are flagged by the MountainPass SR21 campaign. Point npm at the CFS feed mseng/VSJava/vscjava instead. The redirect is carried by npm_config_registry, declared as a pipeline variable. npm resolves configuration in the order cli > environment > project .npmrc > user .npmrc, so writing the registry only into a generated user config would leave it outranked by anything the agent image already configures -- Microsoft hosted images do ship a user level .npmrc pointing at an internal proxy. An environment variable sits above every .npmrc file, so it is the one lever that reaches all of these pipelines. npm matches npm_config_* case insensitively, which matters because restore is not driven by a single task here: the Npm tasks, npx json, npx @vscode/vsce and the vsce invocation inside AzureCLI@2 all inherit the agent environment rather than reading a task input. A .npmrc is still generated into the agent temp directory, because NpmAuthenticate discovers the registries to authenticate by reading it. npm now takes the URL from the environment and the matching credential from that file. Nothing is committed, so open source contributors and the GitHub Actions workflows keep restoring from the public registry, and the credential never lands inside the workspace. Assert the result rather than assuming it. A silent fallback to the public registry is the failure this change exists to prevent and it leaves no trace in the build log -- npm never reports which registry it used, and the package counts look identical either way. The pipelines now fail if npm is not pointed at the CFS feed by the time restore begins. The steps template is referenced as an absolute path anchored to @self. A relative path is resolved against the file doing the including, which for these pipelines is the 1ES extends template in another repository, so the unqualified form is looked up in 1ESPipelineTemplates and fails YAML compilation.
chagong
approved these changes
Jul 28, 2026
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.
Why
SFI Network Isolation requires build pipelines to stop restoring packages directly from public feeds. These pipelines resolve everything from
registry.npmjs.org, so they cannot run on a network isolated pool, and they are flagged by the MountainPass SR21 campaign under ICM 840100965 (ServiceTreea7511cd0-e340-456b-892c-88ce9d7e37ec, deadline 2026-08-21).This points npm at the CFS feed
mseng/VSJava/vscjava.How
The redirect is carried by
npm_config_registry, declared as a pipeline variable. npm resolves configuration in the orderso writing the registry only into a generated user config leaves it at the second lowest precedence, outranked by anything the agent image already configures. Microsoft hosted images do ship a user level
.npmrcpointing at an internal proxy. An environment variable sits above every.npmrcfile, so it is the one lever that reaches all of these pipelines.npm matches
npm_config_*case insensitively, which matters because restore is not driven by a single task here — theNpm@1tasks,npx json,npx @vscode/vsceand the vsce invocation insideAzureCLI@2all inherit the agent environment rather than reading a task input.A
.npmrcis still generated into the agent temp directory, becauseNpmAuthenticate@0discovers the registries to authenticate by reading it. npm now takes the URL from the environment and the matching credential from that file. Nothing is committed, so outside contributors and the GitHub Actions workflows keep restoring from the public registry, and the credential never lands inside the workspace.The outcome is asserted, not assumed. A silent fallback to the public registry is the failure this change exists to prevent, and it leaves no trace in the build log — npm never reports which registry it used and the package counts look identical either way. In vscode-gradle this exact configuration produced a build that restored 718 packages against a CFS feed that had cached nothing. A
Verify CFS npm registrystep now fails the build if npm is not pointed at the feed by the time restore begins.Template references are absolute and anchored to
@self. A relative path is resolved against the file doing the including, which for these pipelines is the 1ES extends template in another repository, so the unqualified form is looked up in1ESPipelineTemplatesand fails YAML compilation.Validation
Full template expansion through the pipelines preview API on this branch. No builds were queued.
VSCode-Test-Runner-Pre-Release-BuildVSCode-Test-Runner-RC-Build1ES-CImicrobuild-testJava-Test-Runner-ReleaseJava-Test-Runner-PreReleaseEach passing pipeline expands to exactly three CFS steps —
Configure CFS npm registry,Authenticate to CFS feed,Verify CFS npm registry— placed after the Node install task and before the first step that restores packages.Feed authentication uses
Project Collection Build Service (mseng), which holds contributor on thevscjavafeed; all of these definitions run withjobAuthScope=projectCollection.The verify script was also run directly under
cmd.exeagainst both outcomes: with a preconfigured user.npmrcin effect it reportshttps://packagefeedproxy.microsoft.io/npm/and exits 1, and withnpm_config_registryset it reports the CFS feed and exits 0 — demonstrating the environment variable overriding a preconfigured user config, which is the case this change exists for.Not covered
NodeTool@0/UseNode@1,JavaToolInstaller@0and anyAPIScantool download are separate outbound fetches and are not addressed here.trigger: noneor disabled.