Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

patch user-level NuGet.Config before invoking dotnet/nuget tools #8748

Merged
merged 8 commits into from
Jan 11, 2024
5 changes: 5 additions & 0 deletions nuget/helpers/build
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ dotnet clean

echo "verifying NuGetUpdater tool"
"$install_dir/NuGetUpdater/NuGetUpdater.Cli" --version

if [ ! -f "$HOME/.nuget/NuGet/NuGet.Config" ]; then
echo "user-level NuGet.Config not found; credential patching will not work"
brettfo marked this conversation as resolved.
Show resolved Hide resolved
exit 1
fi
8 changes: 6 additions & 2 deletions nuget/lib/dependabot/nuget/file_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ def try_update_projects(dependency)

next unless project_dependencies.any? { |dep| dep.name.casecmp(dependency.name).zero? }

NativeHelpers.run_nuget_updater_tool(repo_contents_path, proj_path, dependency, !dependency.top_level?)
NativeHelpers.run_nuget_updater_tool(repo_root: repo_contents_path, proj_path: proj_path,
dependency: dependency, is_transitive: !dependency.top_level?,
credentials: credentials)
brettfo marked this conversation as resolved.
Show resolved Hide resolved
update_ran = true
end

Expand All @@ -77,7 +79,9 @@ def try_update_json(dependency)
project_file = project_files.first
proj_path = dependency_file_path(project_file)

NativeHelpers.run_nuget_updater_tool(repo_contents_path, proj_path, dependency, !dependency.top_level?)
NativeHelpers.run_nuget_updater_tool(repo_root: repo_contents_path, proj_path: proj_path,
dependency: dependency, is_transitive: !dependency.top_level?,
credentials: credentials)
return true
end

Expand Down
11 changes: 7 additions & 4 deletions nuget/lib/dependabot/nuget/native_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# typed: true
# frozen_string_literal: true

require_relative "nuget_config_credential_helpers"

module Dependabot
module Nuget
module NativeHelpers
Expand Down Expand Up @@ -46,7 +48,7 @@ def self.run_nuget_framework_check(project_tfms, package_tfms)
end

# rubocop:disable Metrics/MethodLength
def self.run_nuget_updater_tool(repo_root, proj_path, dependency, is_transitive)
def self.run_nuget_updater_tool(repo_root:, proj_path:, dependency:, is_transitive:, credentials:)
exe_path = File.join(native_helpers_root, "NuGetUpdater", "NuGetUpdater.Cli")
command = [
exe_path,
Expand Down Expand Up @@ -84,9 +86,10 @@ def self.run_nuget_updater_tool(repo_root, proj_path, dependency, is_transitive)

puts "running NuGet updater:\n" + command

output = SharedHelpers.run_shell_command(command, fingerprint: fingerprint)

puts output
NuGetConfigCredentialHelpers.patch_nuget_config_for_action(credentials) do
output = SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
puts output
end
end
# rubocop:enable Metrics/MethodLength
end
Expand Down
71 changes: 71 additions & 0 deletions nuget/lib/dependabot/nuget/nuget_config_credential_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# typed: true
# frozen_string_literal: true

module Dependabot
module Nuget
module NuGetConfigCredentialHelpers
def self.user_nuget_config_path
home_directory = Dir.home
File.join(home_directory, ".nuget", "NuGet", "NuGet.Config")
brettfo marked this conversation as resolved.
Show resolved Hide resolved
end

def self.temporary_nuget_config_path
user_nuget_config_path + "_ORIGINAL"
brettfo marked this conversation as resolved.
Show resolved Hide resolved
end

def self.add_credentials_to_nuget_config(credentials)
return unless File.exist?(user_nuget_config_path)

nuget_credentials = credentials.select { |cred| cred["type"] == "nuget_feed" }
return if nuget_credentials.empty?

File.rename(user_nuget_config_path, temporary_nuget_config_path)

package_sources = []
package_source_credentials = []
nuget_credentials.each_with_index do |c, i|
source_name = "nuget_source_#{i + 1}"
package_sources << " <add key=\"#{source_name}\" value=\"#{c['url']}\" />"
next unless c["token"]

package_source_credentials << " <#{source_name}>"
package_source_credentials << " <add key=\"Username\" value=\"user\" />"
package_source_credentials << " <add key=\"ClearTextPassword\" value=\"#{c['token']}\" />"
package_source_credentials << " </#{source_name}>"
end

nuget_config = <<~NUGET_XML
<?xml version="1.0" encoding="utf-8"?>
brettfo marked this conversation as resolved.
Show resolved Hide resolved
<configuration>
<packageSources>
#{package_sources.join("\n")}
</packageSources>
<packageSourceCredentials>
#{package_source_credentials.join("\n")}
</packageSourceCredentials>
</configuration>
NUGET_XML
File.write(user_nuget_config_path, nuget_config)
end

def self.restore_user_nuget_config
return unless File.exist?(temporary_nuget_config_path)

File.delete(user_nuget_config_path)
File.rename(temporary_nuget_config_path, user_nuget_config_path)
end

# rubocop:disable Lint/SuppressedException
def self.patch_nuget_config_for_action(credentials, &_block)
add_credentials_to_nuget_config(credentials)
begin
yield
rescue StandardError
ensure
restore_user_nuget_config
end
end
# rubocop:enable Lint/SuppressedException
brettfo marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# typed: false
# frozen_string_literal: true

require "dependabot/nuget/nuget_config_credential_helpers"

RSpec.describe Dependabot::Nuget::NuGetConfigCredentialHelpers do
let(:user_nuget_config_contents_during_and_after_action) do
path = Dependabot::Nuget::NuGetConfigCredentialHelpers.user_nuget_config_path
content_during_action = nil
Dependabot::Nuget::NuGetConfigCredentialHelpers.patch_nuget_config_for_action(credentials) do
content_during_action = File.read(path)
end
content_after_action = File.read(path)
{ content_during_action: content_during_action, content_after_action: content_after_action }
end

subject(:result) { user_nuget_config_contents_during_and_after_action }
let(:default_nuget_config_contents) do
File.read(Dependabot::Nuget::NuGetConfigCredentialHelpers.user_nuget_config_path)
end

describe "user level NuGet.Config patching" do
context "with an empty credential set" do
let(:credentials) { [] }

it "does not change the contents of the file" do
expect(result[:content_during_action]).to eq(default_nuget_config_contents)
expect(result[:content_after_action]).to eq(default_nuget_config_contents)
end
end

context "with non-empty credential set" do
let(:credentials) do
[
{ "type" => "nuget_feed", "url" => "https://private.nuget.example.com/index.json",
"token" => "secret_token" },
{ "type" => "nuget_feed", "url" => "https://public.nuget.example.com/index.json" },
{ "type" => "not_nuget", "some_other_field" => "some other value" }
]
end

context "with credentials given" do
it "changes the content of the config file, then changes it back" do
expect(result[:content_during_action]).to eq(
<<~XML
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget_source_1" value="https://private.nuget.example.com/index.json" />
<add key="nuget_source_2" value="https://public.nuget.example.com/index.json" />
</packageSources>
<packageSourceCredentials>
<nuget_source_1>
<add key="Username" value="user" />
<add key="ClearTextPassword" value="secret_token" />
</nuget_source_1>
</packageSourceCredentials>
</configuration>
XML
)
expect(result[:content_after_action]).to eq(default_nuget_config_contents)
end
end

context "when exception is raised" do
it "restores the original file after an exception" do
Dependabot::Nuget::NuGetConfigCredentialHelpers.patch_nuget_config_for_action(credentials) do
raise "This exception was raised when the NuGet.Config file was patched"
end
nuget_config_content = File.read(Dependabot::Nuget::NuGetConfigCredentialHelpers.user_nuget_config_path)
expect(nuget_config_content).to eq(default_nuget_config_contents)
end
end
end
end
end
Loading