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

improve nuget v2 handling for non- nuget.org sources #9172

Merged
merged 3 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
improve nuget v2 handling for non- nuget.org sources
  • Loading branch information
brettfo committed Feb 29, 2024
commit 614399ec95527e426c27c85cbb90fbd16bf27223
11 changes: 6 additions & 5 deletions nuget/lib/dependabot/nuget/nuget_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,15 @@ def self.get_package_versions(dependency_name, repository_details)
doc = execute_xml_nuget_request(repository_details.fetch(:versions_url), repository_details)
return unless doc

id_nodes = doc.xpath("/feed/entry/properties/Id")
# v2 APIs can differ, but all tested have this title value set to the name of the package
title_nodes = doc.xpath("/feed/entry/title")
matching_versions = Set.new
id_nodes.each do |id_node|
return nil unless id_node.text
title_nodes.each do |title_node|
return nil unless title_node.text

next unless id_node.text.casecmp?(dependency_name)
next unless title_node.text.casecmp?(dependency_name)

version_node = id_node.parent.xpath("Version")
version_node = title_node.parent.xpath("properties/Version")
matching_versions << version_node.text if version_node && version_node.text
end

Expand Down
21 changes: 15 additions & 6 deletions nuget/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def self.fetch_nupkg_url_from_repository(repository_details, package_id, package
repository_type = repository_details[:repository_type]

package_url = if repository_type == "v2"
get_nuget_v2_package_url(feed_url, package_id, package_version)
get_nuget_v2_package_url(repository_details, package_id, package_version)
elsif repository_type == "v3"
get_nuget_v3_package_url(repository_details, package_id, package_version)
else
Expand Down Expand Up @@ -90,11 +90,20 @@ def self.get_nuget_v3_package_url_from_search(repository_details, package_id, pa
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity

def self.get_nuget_v2_package_url(feed_url, package_id, package_version)
base_url = feed_url
base_url += "/" unless base_url.end_with?("/")
package_id_downcased = package_id.downcase
"#{base_url}/package/#{package_id_downcased}/#{package_version}"
def self.get_nuget_v2_package_url(repository_details, package_id, package_version)
# get package XML
base_url = repository_details[:base_url].delete_suffix("/")
package_url = "#{base_url}/Packages(Id='#{package_id}',Version='#{package_version}')"
response = fetch_url(package_url, repository_details)
return nil unless response.status == 200

# find relevant element
doc = Nokogiri::XML(response.body)
doc.remove_namespaces!

content_element = doc.xpath("/entry/content")
nupkg_url = content_element&.attribute("src")&.value
nupkg_url
end

def self.fetch_stream(stream_url, auth_header, max_redirects = 5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def build_v2_url(response, repo_details)
base_url: base_url,
repository_url: base_url,
versions_url: File.join(
base_url,
base_url.delete_suffix("/"),
brettfo marked this conversation as resolved.
Show resolved Hide resolved
"FindPackagesById()?id='#{dependency.name}'"
),
auth_header: auth_header_for_token(repo_details.fetch(:token)),
Expand Down
55 changes: 55 additions & 0 deletions nuget/spec/dependabot/nuget/nuget_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,60 @@
expect(package_versions).to eq(Set["0.1.0", "0.1.2"])
end
end

context "versions can be retrieved from v2 apis" do
before do
stub_request(:get, "https://www.nuget.org/api/v2/FindPackagesById()?id=%27Some.Dependency%27")
.to_return(
status: 200,
body:
<<~XML
<?xml version="1.0" encoding="utf-8"?>
<feed xml:base="https://www.nuget.org/api/v2" xmlns="http://www.w3.org/2005/Atom"
xmlns:d="htps://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="htps://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<!-- irrelevant elements omitted -->
<entry>
<title type="text">Some.Dependency</title>
<m:properties>
<!-- n.b., NuGet normally returns a `[d:Id]Some.Dependency[/d:Id]` element here, but not all feeds
report this, so it's intentionally being omitted -->
<d:Version>1.0.0.0</d:Version>
<!-- other irrelevant elements omitted -->
</m:properties>
</entry>
<entry>
<title type="text">Some.Dependency</title>
<m:properties>
<d:Version>1.1.0.0</d:Version>
<!-- other irrelevant elements omitted -->
</m:properties>
</entry>
<entry>
<title type="text">Some.Dependency.But.The.Wrong.One</title>
<m:properties>
<d:Version>1.2.0.0</d:Version>
<!-- other irrelevant elements omitted -->
</m:properties>
</entry>
<feed>
XML
)
end

let(:repository_details) do
{
base_url: "https://www.nuget.org/api/v2",
repository_url: "https://www.nuget.org/api/v2",
versions_url: "https://www.nuget.org/api/v2/FindPackagesById()?id='#{dependency_name}'",
auth_header: {},
repository_type: "v2"
}
end

it "returns the correct version information" do
expect(package_versions).to eq(Set["1.0.0.0", "1.1.0.0"])
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,40 @@
it { is_expected.to eq("https://nuget.pkg.github.com/some-namespace/download/newtonsoft.json/13.0.1/newtonsoft.json.13.0.1.nupkg") }
end

context "with a v2 feed url" do
let(:feed_url) { "https://www.nuget.org/api/v2" }

before do
stub_request(:get, feed_url)
.to_return(
status: 200,
body:
+<<~XML
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="https://www.nuget.org/api/v2">
<workspace>
<collection href="Packages">
<atom:title type="text">Packages</atom:title>
</collection>
</workspace>
</service>
XML
)
stub_request(:get, "https://www.nuget.org/api/v2/Packages(Id='Newtonsoft.Json',Version='13.0.1')")
.to_return(
status: 200,
body:
<<~XML
<entry xmlns:="http://www.w3.org/2005/Atom">
<content type="application/zip" src="https://www.nuget.org/api/v2/Download/Newtonsoft.Json/13.0.1" />
<!-- irrelevant elements omitted -->
</entry>
XML
)
end

it { is_expected.to eq("https://www.nuget.org/api/v2/Download/Newtonsoft.Json/13.0.1") }
end

context "from a v3 feed that doesn't specify `PackageBaseAddress`" do
let(:feed_url) { "https://nuget.example.com/v3-without-package-base/index.json" }

Expand Down
Loading