From 5c083d262b18f724bb2b4a676c922e77e38050ef Mon Sep 17 00:00:00 2001
From: Jeff Brown <375987+jeffpardy@users.noreply.github.com>
Date: Wed, 10 Mar 2021 10:32:15 -0500
Subject: [PATCH] fix(nuget): strip protocolVersion hash from source (#9060)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
---
 lib/datasource/nuget/index.spec.ts            | 40 ++++++++++++++++-
 lib/datasource/nuget/index.ts                 |  2 +-
 .../__snapshots__/artifacts.spec.ts.snap      | 43 ++++++++++++++++++-
 lib/manager/nuget/artifacts.spec.ts           | 22 ++++++++++
 lib/manager/nuget/artifacts.ts                |  5 ++-
 5 files changed, 107 insertions(+), 5 deletions(-)

diff --git a/lib/datasource/nuget/index.spec.ts b/lib/datasource/nuget/index.spec.ts
index cca611cc4c..3170f4c73c 100644
--- a/lib/datasource/nuget/index.spec.ts
+++ b/lib/datasource/nuget/index.spec.ts
@@ -3,7 +3,7 @@ import { getPkgReleases } from '..';
 import * as httpMock from '../../../test/http-mock';
 import * as _hostRules from '../../util/host-rules';
 import { id as versioning } from '../../versioning/nuget';
-import { id as datasource } from '.';
+import { id as datasource, parseRegistryUrl } from '.';
 
 const hostRules: any = _hostRules;
 
@@ -129,6 +129,44 @@ const configV3Multiple = {
 };
 
 describe('datasource/nuget', () => {
+  describe('parseRegistryUrl', () => {
+    beforeEach(() => {
+      jest.resetAllMocks();
+    });
+
+    it('extracts feed version from registry URL hash (v3)', () => {
+      const parsed = parseRegistryUrl('https://my-registry#protocolVersion=3');
+
+      expect(parsed.feedUrl).toEqual('https://my-registry/');
+      expect(parsed.protocolVersion).toEqual(3);
+    });
+
+    it('extracts feed version from registry URL hash (v2)', () => {
+      const parsed = parseRegistryUrl('https://my-registry#protocolVersion=2');
+
+      expect(parsed.feedUrl).toEqual('https://my-registry/');
+      expect(parsed.protocolVersion).toEqual(2);
+    });
+
+    it('defaults to v2', () => {
+      const parsed = parseRegistryUrl('https://my-registry');
+
+      expect(parsed.feedUrl).toEqual('https://my-registry/');
+      expect(parsed.protocolVersion).toEqual(2);
+    });
+
+    it('returns null for unparseable', () => {
+      const parsed = parseRegistryUrl(
+        'https://test:malfor%5Med@test.example.com'
+      );
+
+      expect(parsed.feedUrl).toEqual(
+        'https://test:malfor%5Med@test.example.com'
+      );
+      expect(parsed.protocolVersion).toBeNull();
+    });
+  });
+
   describe('getReleases', () => {
     beforeEach(() => {
       jest.resetAllMocks();
diff --git a/lib/datasource/nuget/index.ts b/lib/datasource/nuget/index.ts
index 215a9e0df0..0b9ebb7936 100644
--- a/lib/datasource/nuget/index.ts
+++ b/lib/datasource/nuget/index.ts
@@ -11,7 +11,7 @@ export const defaultRegistryUrls = [v3.getDefaultFeed()];
 export const defaultVersioning = nugetVersioning.id;
 export const registryStrategy = 'merge';
 
-function parseRegistryUrl(
+export function parseRegistryUrl(
   registryUrl: string
 ): { feedUrl: string; protocolVersion: number } {
   try {
diff --git a/lib/manager/nuget/__snapshots__/artifacts.spec.ts.snap b/lib/manager/nuget/__snapshots__/artifacts.spec.ts.snap
index 648131471c..24488ed845 100644
--- a/lib/manager/nuget/__snapshots__/artifacts.spec.ts.snap
+++ b/lib/manager/nuget/__snapshots__/artifacts.spec.ts.snap
@@ -28,7 +28,7 @@ exports[`updateArtifacts aborts if no lock file found 1`] = `Array []`;
 exports[`updateArtifacts authenticates at registries 1`] = `
 Array [
   Object {
-    "cmd": "dotnet nuget add source https://my-registry.example.org --configfile others/nuget/not-so-random/nuget.config --name myRegistry --username some-username --password some-password --store-password-in-clear-text",
+    "cmd": "dotnet nuget add source https://my-registry.example.org/ --configfile others/nuget/not-so-random/nuget.config --name myRegistry --username some-username --password some-password --store-password-in-clear-text",
     "options": Object {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
@@ -104,6 +104,47 @@ Array [
 ]
 `;
 
+exports[`updateArtifacts strips protocol version from feed url 1`] = `
+Array [
+  Object {
+    "cmd": "dotnet nuget add source https://my-registry.example.org/ --configfile others/nuget/not-so-random/nuget.config --name myRegistry",
+    "options": Object {
+      "cwd": "/tmp/github/some/repo",
+      "encoding": "utf-8",
+      "env": Object {
+        "HOME": "/home/user",
+        "HTTPS_PROXY": "https://example.com",
+        "HTTP_PROXY": "http://example.com",
+        "LANG": "en_US.UTF-8",
+        "LC_ALL": "en_US",
+        "NO_PROXY": "localhost",
+        "PATH": "/tmp/path",
+      },
+      "maxBuffer": 10485760,
+      "timeout": 900000,
+    },
+  },
+  Object {
+    "cmd": "dotnet restore project.csproj --force-evaluate --configfile others/nuget/not-so-random/nuget.config",
+    "options": Object {
+      "cwd": "/tmp/github/some/repo",
+      "encoding": "utf-8",
+      "env": Object {
+        "HOME": "/home/user",
+        "HTTPS_PROXY": "https://example.com",
+        "HTTP_PROXY": "http://example.com",
+        "LANG": "en_US.UTF-8",
+        "LC_ALL": "en_US",
+        "NO_PROXY": "localhost",
+        "PATH": "/tmp/path",
+      },
+      "maxBuffer": 10485760,
+      "timeout": 900000,
+    },
+  },
+]
+`;
+
 exports[`updateArtifacts supports docker mode 1`] = `
 Array [
   Object {
diff --git a/lib/manager/nuget/artifacts.spec.ts b/lib/manager/nuget/artifacts.spec.ts
index 549be8a5fd..1daf2149b6 100644
--- a/lib/manager/nuget/artifacts.spec.ts
+++ b/lib/manager/nuget/artifacts.spec.ts
@@ -227,4 +227,26 @@ describe('updateArtifacts', () => {
     ).not.toBeNull();
     expect(execSnapshots).toMatchSnapshot();
   });
+  it('strips protocol version from feed url', async () => {
+    const execSnapshots = mockExecAll(exec);
+    fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
+    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json' as any);
+    fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json' as any);
+    getConfiguredRegistries.mockResolvedValueOnce([
+      {
+        name: 'myRegistry',
+        url: 'https://my-registry.example.org#protocolVersion=3',
+      },
+    ] as never);
+    hostRules.find.mockImplementationOnce(() => ({}));
+    expect(
+      await nuget.updateArtifacts({
+        packageFileName: 'project.csproj',
+        updatedDeps: ['dep'],
+        newPackageFileContent: '{}',
+        config,
+      })
+    ).not.toBeNull();
+    expect(execSnapshots).toMatchSnapshot();
+  });
 });
diff --git a/lib/manager/nuget/artifacts.ts b/lib/manager/nuget/artifacts.ts
index 6795a2b488..9dbe7e6ada 100644
--- a/lib/manager/nuget/artifacts.ts
+++ b/lib/manager/nuget/artifacts.ts
@@ -1,6 +1,6 @@
 import { join } from 'path';
 import { TEMPORARY_ERROR } from '../../constants/error-messages';
-import { id } from '../../datasource/nuget';
+import { id, parseRegistryUrl } from '../../datasource/nuget';
 import { logger } from '../../logger';
 import { ExecOptions, exec } from '../../util/exec';
 import {
@@ -37,7 +37,8 @@ async function addSourceCmds(
       hostType: id,
       url: registry.url,
     });
-    let addSourceCmd = `dotnet nuget add source ${registry.url} --configfile ${nugetConfigFile}`;
+    const registryInfo = parseRegistryUrl(registry.url);
+    let addSourceCmd = `dotnet nuget add source ${registryInfo.feedUrl} --configfile ${nugetConfigFile}`;
     if (registry.name) {
       // Add name for registry, if known.
       addSourceCmd += ` --name ${registry.name}`;
-- 
GitLab