From c00c234282daefae27781ccd2178a54cfc040636 Mon Sep 17 00:00:00 2001 From: Yeikel Santana Date: Tue, 10 Feb 2026 20:02:07 -0500 Subject: [PATCH] Maven: Ignore repositories from profiles that are not activated --- .../maven/file_parser/repositories_finder.rb | 108 +++++++++++++++--- .../file_parser/repositories_finder_spec.rb | 16 +++ .../custom_repositories_pom_with_profiles.xml | 82 +++++++++++++ 3 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 maven/spec/fixtures/poms/custom_repositories_pom_with_profiles.xml diff --git a/maven/lib/dependabot/maven/file_parser/repositories_finder.rb b/maven/lib/dependabot/maven/file_parser/repositories_finder.rb index 111d6caa2a4..76e9f622968 100644 --- a/maven/lib/dependabot/maven/file_parser/repositories_finder.rb +++ b/maven/lib/dependabot/maven/file_parser/repositories_finder.rb @@ -92,7 +92,7 @@ def super_pom { url: central_repo_url, id: "central" } end - sig { params(entry: Nokogiri::XML::Element).returns(T::Hash[Symbol, T.nilable(String)]) } + sig { params(entry: Nokogiri::XML::Node).returns(T::Hash[Symbol, T.nilable(String)]) } def serialize_mvn_repo(entry) { url: entry.at_css("url").content.strip, @@ -130,22 +130,100 @@ def serialize_urls(entry, pom) .returns(T::Array[T::Hash[Symbol, T.untyped]]) end def gather_repository_urls(pom:, exclude_inherited: false) - repos_in_pom = - Nokogiri::XML(pom.content) - .css(REPOSITORY_SELECTOR) - .map { |node| serialize_mvn_repo(node) } - .reject { |entry| contains_property?(entry[:url]) && !evaluate_properties? } - .select { |entry| entry[:url].start_with?("http") } - .map { |entry| serialize_urls(entry, pom) } - - return repos_in_pom if exclude_inherited - - urls_in_pom = repos_in_pom.map { |repo| repo[:url] } - unless (parent = parent_pom(pom, urls_in_pom)) - return repos_in_pom + repos = repositories_from_pom(pom) + return repos if exclude_inherited + + parent = parent_with_repositories(pom, repos) + return repos unless parent + + repos + gather_repository_urls(pom: parent) + end + + sig do + params( + pom: Dependabot::DependencyFile + ).returns( + T::Array[T::Hash[Symbol, T.untyped]] + ) + end + def repositories_from_pom(pom) + doc = Nokogiri::XML(pom.content) + doc.remove_namespaces! + + repository_nodes(doc) + .filter_map { |node| build_repo_entry(node, pom) } + end + + sig do + params( + node: Nokogiri::XML::Node, + pom: Dependabot::DependencyFile + ).returns(T.nilable(T::Hash[Symbol, T.untyped])) + end + def build_repo_entry(node, pom) + url = node.at_css("url")&.text&.strip.to_s + return if url.empty? + + entry = serialize_mvn_repo(node) + + return if property_blocked?(entry) + return unless http_url?(entry) + + serialize_urls(entry, pom) + end + + sig { params(entry: T::Hash[Symbol, T.nilable(String)]).returns(T::Boolean) } + def property_blocked?(entry) + contains_property?(T.must(entry.fetch(:url))) && !evaluate_properties? + end + + sig { params(entry: T::Hash[Symbol, T.untyped]).returns(T::Boolean) } + def http_url?(entry) + entry.fetch(:url)&.start_with?("http") + end + + sig do + params( + pom: Dependabot::DependencyFile, + repos: T::Array[T::Hash[Symbol, T.untyped]] + ).returns(T.nilable(Dependabot::DependencyFile)) + end + def parent_with_repositories(pom, repos) + urls = repos.map { |r| r[:url] } + parent_pom(pom, urls) + end + + # Returns the repository XML nodes that should be considered when resolving artifacts. + # + # Selection rules: + # - Always includes repositories declared at the project level. + # - Repositories declared inside are included only activated explicitly + # + # @example With active profile + # + # true + # ... + # + # + sig { params(doc: Nokogiri::XML::Document).returns(T::Array[Nokogiri::XML::Node]) } + def repository_nodes(doc) + doc.css(REPOSITORY_SELECTOR).select do |repo_node| + profile = repo_node.ancestors("profile").first + + # Not in a profile => always include + next true unless profile + + # In a profile => only include when activeByDefault=true + active_by_default_profile?(profile) end + end + + sig { params(profile: Nokogiri::XML::Element).returns(T::Boolean) } + def active_by_default_profile?(profile) + node = profile.at_xpath("./activation/activeByDefault") + return false unless node - repos_in_pom + gather_repository_urls(pom: parent) + node.text.strip.casecmp?("true") end sig { returns(T::Boolean) } diff --git a/maven/spec/dependabot/maven/file_parser/repositories_finder_spec.rb b/maven/spec/dependabot/maven/file_parser/repositories_finder_spec.rb index b56b5c1264c..85ce1404418 100644 --- a/maven/spec/dependabot/maven/file_parser/repositories_finder_spec.rb +++ b/maven/spec/dependabot/maven/file_parser/repositories_finder_spec.rb @@ -348,5 +348,21 @@ end end end + + context "when there are repository declarations in profiles" do + let(:base_pom_fixture_name) { "custom_repositories_pom_with_profiles.xml" } + + it "does not include repositories from profiles that are not activated by default" do + expect(repository_urls).to eq( + %w( + https://repo.jenkins-ci.org/public + https://repo.jenkins-ci.org/incrementals-activated + https://repo.jenkins-ci.org/incrementals-activated-2 + https://repo.jenkins-ci.org/another-activated + https://repo.maven.apache.org/maven2 + ) + ) + end + end end end diff --git a/maven/spec/fixtures/poms/custom_repositories_pom_with_profiles.xml b/maven/spec/fixtures/poms/custom_repositories_pom_with_profiles.xml new file mode 100644 index 00000000000..06f10f1150e --- /dev/null +++ b/maven/spec/fixtures/poms/custom_repositories_pom_with_profiles.xml @@ -0,0 +1,82 @@ + + + + 4.0.0 + demo + demo + 0-SNAPSHOT + pom + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + + consume-incrementals + + + incrementals-not-activated + https://repo.jenkins-ci.org/incrementals-disabled/ + + + + + + + true + + incrementals-activated + + + incrementals-activated + https://repo.jenkins-ci.org/incrementals-activated/ + + + incrementals-activated-2 + https://repo.jenkins-ci.org/incrementals-activated-2/ + + + + + + + + TRUE + + another-activated + + + another-activated + https://repo.jenkins-ci.org/another-activated/ + + + + + + + false + + Another profile + + + incrementals + https://repo.jenkins-ci.org/some other repo/ + + + + +