diff --git a/cargo/lib/dependabot/cargo/file_updater/lockfile_updater.rb b/cargo/lib/dependabot/cargo/file_updater/lockfile_updater.rb index 21157d7186..8863357b99 100644 --- a/cargo/lib/dependabot/cargo/file_updater/lockfile_updater.rb +++ b/cargo/lib/dependabot/cargo/file_updater/lockfile_updater.rb @@ -307,7 +307,7 @@ def write_temporary_dependency_files return unless config FileUtils.mkdir_p(File.dirname(T.must(config).name)) - File.write(T.must(config).name, T.must(config).content) + File.write(T.must(config).name, Helpers.sanitize_cargo_config(T.must(T.must(config).content))) end sig { void } diff --git a/cargo/lib/dependabot/cargo/helpers.rb b/cargo/lib/dependabot/cargo/helpers.rb index c409729235..b5c63f6a74 100644 --- a/cargo/lib/dependabot/cargo/helpers.rb +++ b/cargo/lib/dependabot/cargo/helpers.rb @@ -16,6 +16,21 @@ def self.bypass_cargo_credential_providers # shell (along with the appropriate CARGO_REGISTRIES_{NAME}_TOKEN vars) for local development without the proxy. ENV["CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS"] ||= "" end + + sig { params(config_content: String).returns(String) } + def self.sanitize_cargo_config(config_content) + # Remove per-registry `credential-provider` settings from .cargo/config.toml. + # + # Users may configure their repos with lines like: + # [registries.my-registry] + # credential-provider = "cargo:token" + # + # These per-registry settings override the global CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS env var, + # causing Cargo to look up tokens via CARGO_REGISTRIES_{NAME}_TOKEN env vars. Since the dependabot proxy + # handles authentication by intercepting HTTP requests, we need to strip these so Cargo makes plain + # requests that the proxy can decorate with credentials. + config_content.gsub(/^\s*credential-provider\s*=.*$/, "") + end end end end diff --git a/cargo/lib/dependabot/cargo/update_checker/version_resolver.rb b/cargo/lib/dependabot/cargo/update_checker/version_resolver.rb index f30b813fe9..931fbedd3b 100644 --- a/cargo/lib/dependabot/cargo/update_checker/version_resolver.rb +++ b/cargo/lib/dependabot/cargo/update_checker/version_resolver.rb @@ -218,7 +218,7 @@ def write_temporary_dependency_files(prepared: true) return unless config FileUtils.mkdir_p(File.dirname(T.must(config).name)) - File.write(T.must(config).name, T.must(config).content) + File.write(T.must(config).name, Helpers.sanitize_cargo_config(T.must(T.must(config).content))) end sig { void } diff --git a/cargo/spec/dependabot/cargo/helpers_spec.rb b/cargo/spec/dependabot/cargo/helpers_spec.rb index 00fbf10d8f..b6ffadf602 100644 --- a/cargo/spec/dependabot/cargo/helpers_spec.rb +++ b/cargo/spec/dependabot/cargo/helpers_spec.rb @@ -34,4 +34,85 @@ end end end + + describe ".sanitize_cargo_config" do + context "when config has no credential-provider lines" do + let(:config_content) do + <<~TOML + [registries.my-registry] + index = "sparse+https://example.com/index/" + + [registries.another-registry] + index = "sparse+https://other.example.com/index/" + TOML + end + + it "returns the content unchanged" do + result = described_class.sanitize_cargo_config(config_content) + expect(result).to eq(config_content) + end + end + + context "when config has per-registry credential-provider lines" do + let(:config_content) do + <<~TOML + [registries.artifactory] + index = "sparse+https://example.com/api/cargo/cargo-local/index/" + credential-provider = "cargo:token" + + [registries.artifactory-remote] + index = "sparse+https://example.com/api/cargo/cargo-crates-remote/index/" + credential-provider = "cargo:token" + TOML + end + + it "strips the credential-provider lines" do + result = described_class.sanitize_cargo_config(config_content) + expect(result).not_to include("credential-provider") + expect(result).to include('index = "sparse+https://example.com/api/cargo/cargo-local/index/"') + expect(result).to include('index = "sparse+https://example.com/api/cargo/cargo-crates-remote/index/"') + end + end + + context "when config has credential-provider with different spacing" do + let(:config_content) do + <<~TOML + [registries.my-registry] + index = "sparse+https://example.com/index/" + credential-provider = "cargo:token" + TOML + end + + it "strips the credential-provider line regardless of whitespace" do + result = described_class.sanitize_cargo_config(config_content) + expect(result).not_to include("credential-provider") + expect(result).to include('index = "sparse+https://example.com/index/"') + end + end + + context "when config has mixed registries with and without credential-provider" do + let(:config_content) do + <<~TOML + [registries.with-cred] + index = "sparse+https://example.com/index/" + credential-provider = "cargo:token" + + [registries.without-cred] + index = "sparse+https://other.example.com/index/" + + [source.crates-io] + replace-with = "with-cred" + TOML + end + + it "strips only the credential-provider lines" do + result = described_class.sanitize_cargo_config(config_content) + expect(result).not_to include("credential-provider") + expect(result).to include('[registries.with-cred]') + expect(result).to include('[registries.without-cred]') + expect(result).to include('[source.crates-io]') + expect(result).to include('replace-with = "with-cred"') + end + end + end end