Skip to content

Route update_appsettings version lookups through private Azure Artifacts feed#7985

Open
gavinbarron wants to merge 22 commits into
mainfrom
gavinbarron-miniature-train
Open

Route update_appsettings version lookups through private Azure Artifacts feed#7985
gavinbarron wants to merge 22 commits into
mainfrom
gavinbarron-miniature-train

Conversation

@gavinbarron

Copy link
Copy Markdown
Contributor

Problem

The update_appsettings job in .azure-pipelines/ci-build.yml runs scripts/update-versions.ps1, which is the only step in that job making external network calls. The script queried public package registries directly via Invoke-RestMethod (api.nuget.org, registry.npmjs.org, pypi.org, repo1.maven.org/repo.maven.apache.org, repo.packagist.org, pub.dev), bypassing the authenticated private feed. With ~60 dependency lookups wrapped in a 5-retry helper, this accounted for the 350+ flagged unauthenticated egress requests.

Change

Repoint the version lookups that can be proxied at the authenticated GraphDeveloperExperiences_Public Azure Artifacts feed, which has upstream sources configured for NuGet, npm, PyPI and Maven Central:

  • scripts/update-versions.ps1

    • New param() block: -NuGetServiceIndexUrl, -NpmRegistryUrl, -PyPiSimpleIndexUrl, -MavenRepositoryUrl, -FeedAccessToken (defaults are the public registries, so local/dev runs are unchanged).
    • Adds a Bearer auth header when a token is supplied.
    • NuGet: resolves the registrations base from the service index (RegistrationsBaseUrl/3.6.0, cached) rather than a hard-coded registration5-gz-semver2 path — verified it still resolves to the same endpoint.
    • PyPI: switches to the PEP 691 JSON simple index, since Azure Artifacts does not expose the legacy pypi.org/pypi/{pkg}/json API. Picks the highest stable version.
    • npm / Maven: base-URL parameterization + auth headers.
    • Go, PHP, Ruby, Dart lookups are left on public registries — Azure Artifacts has no upstream source type for Packagist, pub.dev, GitHub releases or RubyGems.
  • .azure-pipelines/ci-build.yml (update_appsettings job)

    • Adds a privateFeedBaseUrl variable and passes the feed endpoints to the script.
    • Maps FEED_ACCESS_TOKEN: $(System.AccessToken) so the calls authenticate as the Project Build Service identity (the same identity the build job already uses to reach this feed).

Validation

  • PowerShell AST parse: OK.
  • Rewritten NuGet and PyPI resolvers tested live against the public defaults (returned expected versions).
  • Confirmed only the two intended files change; appsettings.json is untouched.

Notes for reviewers

  • Feed URLs were provided/confirmed for npm (/npm/registry), PyPI (/pypi/simple) and Maven (/maven/v1) on the GraphDeveloperExperiences_Public feed.
  • No auth task needed: System.AccessToken is intrinsically available; NuGetAuthenticate@1 only configures the NuGet credential provider (used by dotnet restore), not raw Invoke-RestMethod, so it isn't required here.
  • Packagist (PHP) and pub.dev (Dart) egress will remain and should be allow-listed, as agreed.

…cts feed

The update_appsettings job's update-versions.ps1 queried public package registries (NuGet, npm, PyPI, Maven, etc.) directly, causing 350+ unauthenticated egress requests. Repoint the NuGet, npm, PyPI and Maven lookups at the authenticated GraphDeveloperExperiences_Public Azure Artifacts feed (which has upstream sources for those ecosystems), authenticating with the pipeline System.AccessToken.

PyPI switches to the PEP 691 JSON simple index since Azure Artifacts does not expose the legacy pypi.org JSON API. NuGet resolves the registrations base from the service index instead of a hard-coded path. PHP (Packagist), Dart (pub.dev), Go and Ruby have no Azure Artifacts upstream type and remain on public registries. Feed URLs default to the public registries so local runs are unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 23, 2026 18:46
@gavinbarron
gavinbarron requested a review from a team as a code owner July 23, 2026 18:46
@msgraph-bot msgraph-bot Bot added this to Kiota Jul 23, 2026

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

This PR reduces unauthenticated CI egress by routing update_appsettings dependency-version lookups (NuGet/npm/PyPI/Maven) through an authenticated Azure Artifacts feed with upstream sources, instead of calling public registries directly.

Changes:

  • Parameterizes registry/service base URLs in scripts/update-versions.ps1 and adds optional feed authentication headers.
  • Updates NuGet lookup to resolve the registrations base URL from the NuGet service index (cached) rather than hard-coding a registrations endpoint.
  • Updates the update_appsettings Azure Pipeline job to pass private feed endpoints and provide the OAuth token via environment variable.

Reviewed changes

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

File Description
scripts/update-versions.ps1 Adds feed endpoint parameters + auth headers; routes NuGet/npm/PyPI/Maven version lookups through configurable endpoints.
.azure-pipelines/ci-build.yml Passes Azure Artifacts feed URLs and System.AccessToken into the update script for authenticated lookups.
Comments suppressed due to low confidence (1)

scripts/update-versions.ps1:114

  • Casting PyPI versions to [version] can throw for valid numeric versions with more than 4 dot-separated components (PEP 440 allows arbitrary release segment lengths). This would fail the whole update job. Prefer a numeric-segment sort that doesn’t rely on System.Version.
    $stableVersions = $response.versions | Where-Object { $_ -match '^[0-9]+(\.[0-9]+)*$' }
    if ($stableVersions) {
        ($stableVersions | ForEach-Object { [version]$_ } | Sort-Object | Select-Object -Last 1).ToString()
    }

Comment thread scripts/update-versions.ps1 Outdated
Comment thread scripts/update-versions.ps1 Outdated
@github-code-quality

github-code-quality Bot commented Jul 23, 2026

Copy link
Copy Markdown

Code Coverage Overview

Languages: C#

C# / code-coverage/dotnet

The overall coverage in commit 8e38d5b in the gavinbarron-miniatur... branch is 72%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main gavinbarron-miniatur... 8e38d5b +/-
/home/runner/wo...guageRefiner.cs 98%
/home/runner/wo...criptRefiner.cs 98%
/home/runner/wo...MethodWriter.cs 97%
/home/runner/wo...MethodWriter.cs 96%
/home/runner/wo...MethodWriter.cs 96%
/home/runner/wo...MethodWriter.cs 95%
/home/runner/wo...rs/GoRefiner.cs 94%
/home/runner/wo...KiotaBuilder.cs 90%
/home/runner/wo...ationService.cs 89%
/home/runner/wo...xGenerator.g.cs 75%

Updated July 26, 2026 20:58 UTC

The *_build_binaries jobs run 'dotnet publish' without authenticating to
or pointing at the GraphDeveloperExperiences_Public Azure Artifacts feed,
so NuGet restore fell back to the public api.nuget.org and failed network
isolation. Add NuGetAuthenticate@1 and generate a private-feed nuget.config
before the publish steps, mirroring the existing build job.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 23, 2026 19:08

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

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

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:25

  • FEED_ACCESS_TOKEN is turned into a global Authorization header unconditionally when set, and then reused for all NuGet/npm/PyPI/Maven requests. If someone runs this script with a token but keeps the default public registry URLs, this will send the private feed token to public hosts (api.nuget.org / registry.npmjs.org / pypi.org / repo1.maven.org), which is a credential-leak risk. Consider only attaching the Bearer header when the request target is an Azure Artifacts host (pkgs.visualstudio.com / pkgs.dev.azure.com) or when an explicit "use auth" switch is provided, and keep unauthenticated requests for public endpoints even if FEED_ACCESS_TOKEN is present.
# Build the auth headers once. Azure Artifacts protocol endpoints accept the pipeline OAuth token as a Bearer token.
$script:FeedAuthHeaders = @{}
if (-not [string]::IsNullOrWhiteSpace($FeedAccessToken)) {
    $script:FeedAuthHeaders["Authorization"] = "Bearer $FeedAccessToken"
}

scripts/update-versions.ps1:106

  • PyPI package name normalization is not fully PEP 503 compliant: it should replace any run of '-', '' or '.' with a single '-' (not just '' and '.'). Without this, some package IDs could map to the wrong simple-index URL.
    $normalizedId = $packageId.ToLowerInvariant().Replace("_", "-").Replace(".", "-")
    $url = "$PyPiSimpleIndexUrl/$normalizedId/"

Comment thread scripts/update-versions.ps1
Comment thread .azure-pipelines/ci-build.yml Outdated
Comment thread .azure-pipelines/ci-build.yml Outdated
The 1ES Official template injects Component Governance / Component Detection
into build-stage jobs (including *_build_binaries). Its Maven detector runs
'mvn dependency:tree' against the it/java and it/mockserver pom.xml fixtures,
reaching repo.maven.apache.org, and other per-language detectors resolve the
it/ fixtures against their public registries -- all failing network isolation.
These fixtures are integration-test artifacts, not dependencies of the shipped
kiota binary, so exclude the it/ directory via sdl.componentgovernance.ignoreDirectories.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 23, 2026 19:17

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:105

  • PEP 503 name normalization collapses any run of '-', '' and '.' into a single '-'. The current normalization only replaces '' and '.', which can produce incorrect project URLs (e.g. multiple consecutive separators) and lead to 404s.
    $normalizedId = $packageId.ToLowerInvariant().Replace("_", "-").Replace(".", "-")

.azure-pipelines/ci-build.yml:507

  • The Azure Artifacts feed URL is hard-coded here (and elsewhere in this file). That makes future feed/org/project changes error-prone because multiple jobs need to be updated in sync. Consider promoting the feed base URL to a single pipeline-level variable and reusing it for all nuget.config creation blocks and for the update-versions parameters.
                          <clear />
                          <add key="GraphDeveloperExperiences_Public" value="https://microsoftgraph.pkgs.visualstudio.com/0985d294-5762-4bc2-a565-161ef349ca3e/_packaging/GraphDeveloperExperiences_Public/nuget/v3/index.json" />
                        </packageSources>

Comment thread scripts/update-versions.ps1
Azure Artifacts npm feeds do not implement the /{package}/latest dist-tag
shortcut and return 404 for it. Fetch the full packument at /{package} and
read dist-tags.latest instead, which works against both the private feed and
public registry.npmjs.org. Scoped names have their '/' encoded as %2F.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 23, 2026 20:23

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

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

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:122

  • In the PyPI resolver, casting version strings to [version] can throw for numeric versions with more than 4 dot-separated components (the regex allows any length). Also, when no “stable” versions match the regex, Select-Object -Last 1 relies on whatever ordering the server returns, which isn’t guaranteed to be highest.

Consider sorting numeric versions without [version] (e.g., by zero-padding numeric segments) and at least sorting the fallback list to avoid order-dependent results.

    # Prefer the highest final (non pre-release) version to match the previous behaviour of pypi's info.version.
    $stableVersions = $response.versions | Where-Object { $_ -match '^[0-9]+(\.[0-9]+)*$' }
    if ($stableVersions) {
        ($stableVersions | ForEach-Object { [version]$_ } | Sort-Object | Select-Object -Last 1).ToString()
    }
    else {
        $response.versions | Select-Object -Last 1
    }

.azure-pipelines/ci-build.yml:508

  • This block hard-codes the GraphDeveloperExperiences_Public feed URL again, duplicating the same URL already embedded earlier in the pipeline. This increases the risk of drift if the feed/org/project changes.

Consider defining a single pipeline-level variable for the feed base URL (or NuGet index URL) and reusing it in both nuget.config generation blocks (and in update_appsettings), so there’s only one place to update.

                        <packageSources>
                          <clear />
                          <add key="GraphDeveloperExperiences_Public" value="https://microsoftgraph.pkgs.visualstudio.com/0985d294-5762-4bc2-a565-161ef349ca3e/_packaging/GraphDeveloperExperiences_Public/nuget/v3/index.json" />
                        </packageSources>
                      </configuration>

Component Detection --IgnoreDirectories matches comma-separated absolute paths
against its scan root, which can be either Build.SourcesDirectory or
Build.Repository.LocalPath depending on the injected job. Listing the it/
integration-test fixtures under only one root let the Maven detector still run
mvn dependency:tree against it/**/pom.xml, egressing to repo.maven.apache.org
and failing network isolation in the update_appsettings job. List both roots so
the exclusion always matches.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 23, 2026 21:09

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

scripts/update-versions.ps1:112

  • PEP 503 name normalization requires collapsing any run of [-_.]+ to a single -. The current logic only replaces _ and ., so a name like my__pkg would become my--pkg and likely 404 on the simple index.
    $normalizedId = $packageId.ToLowerInvariant().Replace("_", "-").Replace(".", "-")
    $url = "$PyPiSimpleIndexUrl/$normalizedId/"
    $headers = $script:FeedAuthHeaders.Clone()

scripts/update-versions.ps1:122

  • When no versions match the numeric-only “stable” regex, the code returns the last entry in $response.versions. The PEP 691 JSON response does not guarantee ordering, so this can return a non-latest version nondeterministically. Sort before selecting.
    # Prefer the highest final (non pre-release) version to match the previous behaviour of pypi's info.version.
    $stableVersions = $response.versions | Where-Object { $_ -match '^[0-9]+(\.[0-9]+)*$' }
    if ($stableVersions) {
        ($stableVersions | ForEach-Object { [version]$_ } | Sort-Object | Select-Object -Last 1).ToString()
    }
    else {
        $response.versions | Select-Object -Last 1
    }

.azure-pipelines/ci-build.yml:514

  • The PR description calls out .azure-pipelines/ci-build.yml changes only for the update_appsettings job, but this diff also adds NuGetAuthenticate@1 + a generated nuget.config in a different job. If these steps are required for the same network-isolation goal, the PR description should be updated to include them; otherwise consider moving them to a separate PR to keep scope tight.
                  - task: NuGetAuthenticate@1
                    displayName: 'Authenticate to Azure Artifacts'

                  - pwsh: |
                      @"
                      <?xml version="1.0" encoding="utf-8"?>
                      <configuration>
                        <packageSources>
                          <clear />
                          <add key="GraphDeveloperExperiences_Public" value="https://microsoftgraph.pkgs.visualstudio.com/0985d294-5762-4bc2-a565-161ef349ca3e/_packaging/GraphDeveloperExperiences_Public/nuget/v3/index.json" />
                        </packageSources>
                      </configuration>
                      "@ | Set-Content -Path "$(Build.SourcesDirectory)/nuget.config" -Encoding UTF8
                    displayName: 'Create nuget.config (central feed)'

Comment thread scripts/update-versions.ps1
Set parameters.settings.networkIsolationPolicy to "Permissive,CFSClean" on the
1ES Official template so the pipeline enforces Central Feed Service isolation:
package restores are constrained to approved/authenticated feeds and direct
calls to public registries are blocked. Complements routing package-version
lookups and NuGet restores through the GraphDeveloperExperiences_Public feed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 23, 2026 22:39

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:25

  • The script always adds an Authorization header whenever FEED_ACCESS_TOKEN is set, even when targeting the default public registries. This can unintentionally send the pipeline OAuth token (or any user token in the environment) to api.nuget.org/registry.npmjs.org/pypi.org/repo1.maven.org during local/dev runs or misconfigured CI runs.
# Build the auth headers once. Azure Artifacts protocol endpoints accept the pipeline OAuth token as a Bearer token.
$script:FeedAuthHeaders = @{}
if (-not [string]::IsNullOrWhiteSpace($FeedAccessToken)) {
    $script:FeedAuthHeaders["Authorization"] = "Bearer $FeedAccessToken"
}

scripts/update-versions.ps1:110

  • PyPI project-name normalization for the simple index should collapse any run of '-', '' and '.' into a single '-', per PEP 503. The current Replace("", "-").Replace(".", "-") can produce double dashes (e.g. "a..b" -> "a--b"), which can break lookups against /simple/{normalized-name}/.
    $normalizedId = $packageId.ToLowerInvariant().Replace("_", "-").Replace(".", "-")

Comment thread .azure-pipelines/ci-build.yml Outdated
Set networkIsolationPolicy to "CFSClean" only (removing the "Permissive" base)
so calls to banned public endpoints fail the run instead of being allowed. This
surfaces exactly which steps still egress to public registries for diagnosis.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 24, 2026 02:22

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:110

  • PyPI project-name normalization is incomplete for PEP 503/691: it replaces '' and '.' but does not collapse runs of mixed separators (e.g. "a..b" or "a-b") into a single '-'. Some indexes (including proxies) can be strict about normalized names, leading to 404s for valid packages. Use the canonical normalization regex.
    $normalizedId = $packageId.ToLowerInvariant().Replace("_", "-").Replace(".", "-")

scripts/update-versions.ps1:116

  • PEP 691 JSON responses don’t expose a top-level versions array; versions are per-file under files. As written, $response.versions will be $null against PyPI/Azure Artifacts and the function will return nothing (or throw later). Derive versions from $response.files.version and then select the latest.
    $stableVersions = $response.versions | Where-Object { $_ -match '^[0-9]+(\.[0-9]+)*$' }

Comment thread .azure-pipelines/ci-build.yml
Nothing in this pipeline builds or runs the it/ integration-test fixtures, but
their per-language manifests cause Component Governance to reach public
registries under network isolation. Delete it/ with git rm right after each
checkout so no later scan or build sees it. Uses a plain script/git invocation
(no pwsh) so it also works on the win-arm64 host before PowerShell is bootstrapped.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 25, 2026 00:27

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

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

Comments suppressed due to low confidence (3)

scripts/update-versions.ps1:42

  • NuGet service index entries can have an '@type' that is either a string or an array of strings. Using -eq against $type will fail to match when the feed returns an array (which would make version lookups break even though the resource exists).
    $preferredTypes = @("RegistrationsBaseUrl/3.6.0", "RegistrationsBaseUrl/Versioned", "RegistrationsBaseUrl")
    foreach ($type in $preferredTypes) {
        $resource = $index.resources | Where-Object { $_.'@type' -eq $type } | Select-Object -First 1
        if ($null -ne $resource) {
            $script:NuGetRegistrationsBaseUrl = $resource.'@id'.TrimEnd('/')

scripts/update-versions.ps1:118

  • The PEP 691 JSON response shape is not guaranteed to include a top-level versions array (commonly it returns files). As written, $response.versions can be $null, causing the function to return $null (or pick an arbitrary value) and potentially write an empty dependency version into appsettings.
    $response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
    # Prefer the highest final (non pre-release) version to match the previous behaviour of pypi's info.version.
    $stableVersions = $response.versions | Where-Object { $_ -match '^[0-9]+(\.[0-9]+)*$' }
    if ($stableVersions) {
        ($stableVersions | ForEach-Object { [version]$_ } | Sort-Object | Select-Object -Last 1).ToString()

.azure-pipelines/ci-build.yml:122

  • The PR description says the pipeline change is limited to the update_appsettings job, but this diff also modifies global 1ES template settings (component governance ignoreDirectories) and adds multiple new steps across other jobs (e.g. removing it/, tool installs via custom nuget.config, bootstrapping PowerShell on arm64). Please either update the PR description to reflect the broader scope or split these unrelated pipeline changes into separate PR(s) so the network-isolation work is easier to review and revert independently.
      componentgovernance:
        # The it/ directory holds per-language integration-test fixtures (Java/Maven, PHP/Composer,
        # Dart/Pub, Python, Go, Ruby, TypeScript). Component Detection would otherwise resolve those
        # manifests against public registries (e.g. the Maven detector runs `mvn dependency:tree`,
        # reaching repo.maven.apache.org), which fails network isolation. These fixtures are not
        # dependencies of the shipped kiota binary, so exclude them from CG scanning.
        # ignoreDirectories maps to Component Detection's --IgnoreDirectories (comma-separated absolute
        # paths, matched against the scan root — not globs). The scan root can be either
        # $(Build.SourcesDirectory) or $(Build.Repository.LocalPath) depending on the injected job, so
        # list the fixture directory under both roots to guarantee the exclusion matches.
        ignoreDirectories: "$(Build.SourcesDirectory)/it,$(Build.Repository.LocalPath)/it"

The it/ fixtures are now deleted right after checkout, so the Component
Governance ignoreDirectories exclusion is no longer needed - remove it.

Promote privateFeedBaseUrl to the pipeline-level variables block and reference
it as $(privateFeedBaseUrl) everywhere (the binary-build nuget.config sources
previously hard-coded the raw feed URL), so the feed URL is defined in exactly
one place.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 26, 2026 20:04

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

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

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:25

  • FeedAccessToken is applied to all version-lookup requests via $script:FeedAuthHeaders. Because the default registry URLs point to public endpoints (api.nuget.org/registry.npmjs.org/pypi.org/repo1.maven.org), this can unintentionally send the pipeline OAuth token to third-party hosts when the env var is set, which is a credential-leak risk. Consider only attaching the Authorization header when the target base URLs are Azure Artifacts (pkgs.visualstudio.com / pkgs.dev.azure.com).
# Build the auth headers once. Azure Artifacts protocol endpoints accept the pipeline OAuth token as a Bearer token.
$script:FeedAuthHeaders = @{}
if (-not [string]::IsNullOrWhiteSpace($FeedAccessToken)) {
    $script:FeedAuthHeaders["Authorization"] = "Bearer $FeedAccessToken"
}

.azure-pipelines/ci-build.yml:156

  • This job relies on FEED_ACCESS_TOKEN: $(System.AccessToken) for authenticated calls to the Azure Artifacts feed endpoints. In Azure Pipelines, System.AccessToken is empty unless the job explicitly allows scripts to access the OAuth token (for example via checkout: persistCredentials: true or the equivalent job setting). If it's empty, the update-versions.ps1 calls to the private feed will 401 and the job will fail.
              - pwsh: >
                  $(Build.SourcesDirectory)/scripts/update-versions.ps1
                  -NuGetServiceIndexUrl "$(privateFeedBaseUrl)/nuget/v3/index.json"
                  -NpmRegistryUrl "$(privateFeedBaseUrl)/npm/registry"
                  -PyPiSimpleIndexUrl "$(privateFeedBaseUrl)/pypi/simple"
                  -MavenRepositoryUrl "$(privateFeedBaseUrl)/maven/v1"
                displayName: "Update dependencies versions"
                name: update_versions
                env:
                  FEED_ACCESS_TOKEN: $(System.AccessToken)

The second update-versions.ps1 invocation (PushDockerImage job) was called with
no feed arguments, so it resolved package versions against public registries.
Pass the same $(privateFeedBaseUrl) NuGet/npm/PyPI/Maven URLs and
FEED_ACCESS_TOKEN as the update_appsettings job so it uses the authenticated
Azure Artifacts feed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 26, 2026 20:10

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

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

Comments suppressed due to low confidence (1)

scripts/update-versions.ps1:25

  • The script unconditionally adds the Bearer token to $script:FeedAuthHeaders when $FeedAccessToken is set, and those headers are then used for NuGet/npm/PyPI/Maven requests regardless of whether the configured URLs point to Azure Artifacts or public registries. If someone runs the script with FEED_ACCESS_TOKEN set but leaves the default public endpoints, this would send the pipeline token to public third-party services (credential exfiltration risk).
# Build the auth headers once. Azure Artifacts protocol endpoints accept the pipeline OAuth token as a Bearer token.
$script:FeedAuthHeaders = @{}
if (-not [string]::IsNullOrWhiteSpace($FeedAccessToken)) {
    $script:FeedAuthHeaders["Authorization"] = "Bearer $FeedAccessToken"
}

update-versions.ps1 built one Authorization header and attached it to every
NuGet/npm/PyPI/Maven request, so when a feed URL was left at its public default
(api.nuget.org, npmjs.org, pypi.org, Maven Central) the pipeline OAuth token was
sent to a third-party registry. Decide per-feed instead: attach the Bearer token
only when the target URL is a private Azure Artifacts feed (pkgs.dev.azure.com or
*.pkgs.visualstudio.com) and a token is available; public registries get no auth
header. The host check is exact/suffix-anchored so lookalike hosts do not match.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 26, 2026 20:23

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:145

  • Get-LatestPypiVersion assumes the PEP 691 JSON response has a top-level versions property, but the Simple API JSON payload is commonly meta/name/files only. This will return $null (or throw later) and break version updates in CI when hitting Azure Artifacts' PyPI upstream.
    $stableVersions = $response.versions | Where-Object { $_ -match '^[0-9]+(\.[0-9]+)*$' }

scripts/update-versions.ps1:54

  • When any of the registry URLs point at Azure Artifacts, the script requires FEED_ACCESS_TOKEN (or -FeedAccessToken) to avoid repeated 401s and a generic Execution failed. error. Add a fast-fail with a clear message so CI failures are actionable.
$script:NuGetAuthHeaders = Get-FeedAuthHeaders -Url $NuGetServiceIndexUrl
$script:NpmAuthHeaders = Get-FeedAuthHeaders -Url $NpmRegistryUrl
$script:PyPiAuthHeaders = Get-FeedAuthHeaders -Url $PyPiSimpleIndexUrl
$script:MavenAuthHeaders = Get-FeedAuthHeaders -Url $MavenRepositoryUrl

Comment thread scripts/update-versions.ps1
The PyPI lookup normalized project names with per-character Replace() calls
(_ -> -, . -> -), which does not collapse runs of separators the way PEP 503
requires (re.sub(r\"[-_.]+\", \"-\", name).lower()). Names like \"a..b\" became
\"a--b\" and existing \"--\" runs were left intact, yielding a non-canonical
name that the simple index returns 404 for. Replace with a single regex that
collapses any run of -, _ or . into one - before lowercasing. Current kiota
Python package names are unaffected.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 26, 2026 20:31

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

scripts/update-versions.ps1:71

  • NuGet service index entries can represent @type as an array (or multiple types). Using -eq can fail to match and will incorrectly throw even though a RegistrationsBaseUrl resource exists. Wrap the value in @(...) and use -contains to support both scalar and array forms.
    $preferredTypes = @("RegistrationsBaseUrl/3.6.0", "RegistrationsBaseUrl/Versioned", "RegistrationsBaseUrl")
    foreach ($type in $preferredTypes) {
        $resource = $index.resources | Where-Object { $_.'@type' -eq $type } | Select-Object -First 1
        if ($null -ne $resource) {
            $script:NuGetRegistrationsBaseUrl = $resource.'@id'.TrimEnd('/')

scripts/update-versions.ps1:48

  • When an access token is supplied, the script should refuse to attach it to non-HTTPS feed URLs. As written, a misconfigured http://...pkgs... URL would silently send the bearer token over cleartext.
function Get-FeedAuthHeaders {
    param([string]$Url)
    if ($script:HasFeedAccessToken -and (Test-IsAzureArtifactsUrl -Url $Url)) {
        return @{ "Authorization" = "Bearer $FeedAccessToken" }
    }
    return @{}
}

scripts/update-versions.ps1:154

  • The PEP 691 JSON simple project response is not guaranteed to expose a top-level versions array (some indexes only return files). Relying on $response.versions can yield $null and cause the script to set an empty dependency version. Consider falling back to extracting numeric versions from returned filenames and failing fast if no versions can be determined.
    $response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
    # Prefer the highest final (non pre-release) version to match the previous behaviour of pypi's info.version.
    $stableVersions = $response.versions | Where-Object { $_ -match '^[0-9]+(\.[0-9]+)*$' }
    if ($stableVersions) {
        ($stableVersions | ForEach-Object { [version]$_ } | Sort-Object | Select-Object -Last 1).ToString()
    }
    else {
        $response.versions | Select-Object -Last 1
    }

Comment thread .azure-pipelines/ci-build.yml
The Python resolver read $response.versions, but the top-level versions array
is a PEP 700 addition that PEP 691 does not guarantee; against a feed that
implements PEP 691 only, versions is null and selection breaks. Recover the
candidate version list from the distribution filenames in files[] when versions
is absent. Also drop the [version] cast (limited to four segments and throws on
longer ones) in favour of a zero-padded, cast-free numeric sort so selection is
robust and deterministic.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 26, 2026 20:44

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

scripts/update-versions.ps1:38

  • Test-IsAzureArtifactsUrl uses string.EndsWith(".pkgs.visualstudio.com") which is case-sensitive, and it does not require an https scheme. A differently-cased host could prevent auth headers from being attached (breaking private feed calls), and allowing non-https URLs risks sending the bearer token over plaintext if someone passes an http URL.
function Test-IsAzureArtifactsUrl {
    param([string]$Url)
    if ([string]::IsNullOrWhiteSpace($Url)) { return $false }
    try {
        $feedHost = ([uri]$Url).Host
    }
    catch {
        return $false
    }
    return $feedHost -eq "pkgs.dev.azure.com" -or $feedHost.EndsWith(".pkgs.visualstudio.com")
}

scripts/update-versions.ps1:71

  • NuGet service index resources can expose '@type' as an array (multiple types per resource). Using -eq against a scalar can fail to find RegistrationsBaseUrl and cause the version lookup to throw even though the index is valid (especially against non-nuget.org feeds).
    $preferredTypes = @("RegistrationsBaseUrl/3.6.0", "RegistrationsBaseUrl/Versioned", "RegistrationsBaseUrl")
    foreach ($type in $preferredTypes) {
        $resource = $index.resources | Where-Object { $_.'@type' -eq $type } | Select-Object -First 1
        if ($null -ne $resource) {
            $script:NuGetRegistrationsBaseUrl = $resource.'@id'.TrimEnd('/')

Comment thread scripts/update-versions.ps1 Outdated
…filename

The filename fallback assumed the normalized project name contained no hyphen and took the second hyphen-separated field as the version. That is wrong for legacy sdists whose name keeps hyphens (e.g. microsoft-kiota-abstractions-1.10.1.tar.gz). The parser now strips the known project name (separator-tolerant) so the version is the field that follows it, falling back to the old heuristic only when the name is unknown.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bfe57333-036a-47d6-a9e4-9917ba6bc414
Copilot AI review requested due to automatic review settings July 26, 2026 20:53

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread scripts/update-versions.ps1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress 🚧

Development

Successfully merging this pull request may close these issues.

3 participants