Skip to content

Commit ab5620b

Browse files
committed
Maven: Ignore repositories from profiles that are not activated
1 parent 2b94ccf commit ab5620b

File tree

3 files changed

+191
-15
lines changed

3 files changed

+191
-15
lines changed

maven/lib/dependabot/maven/file_parser/repositories_finder.rb

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def super_pom
9292
{ url: central_repo_url, id: "central" }
9393
end
9494

95-
sig { params(entry: Nokogiri::XML::Element).returns(T::Hash[Symbol, T.nilable(String)]) }
95+
sig { params(entry: Nokogiri::XML::Node).returns(T::Hash[Symbol, T.nilable(String)]) }
9696
def serialize_mvn_repo(entry)
9797
{
9898
url: entry.at_css("url").content.strip,
@@ -130,22 +130,100 @@ def serialize_urls(entry, pom)
130130
.returns(T::Array[T::Hash[Symbol, T.untyped]])
131131
end
132132
def gather_repository_urls(pom:, exclude_inherited: false)
133-
repos_in_pom =
134-
Nokogiri::XML(pom.content)
135-
.css(REPOSITORY_SELECTOR)
136-
.map { |node| serialize_mvn_repo(node) }
137-
.reject { |entry| contains_property?(entry[:url]) && !evaluate_properties? }
138-
.select { |entry| entry[:url].start_with?("http") }
139-
.map { |entry| serialize_urls(entry, pom) }
140-
141-
return repos_in_pom if exclude_inherited
142-
143-
urls_in_pom = repos_in_pom.map { |repo| repo[:url] }
144-
unless (parent = parent_pom(pom, urls_in_pom))
145-
return repos_in_pom
133+
repos = repositories_from_pom(pom)
134+
return repos if exclude_inherited
135+
136+
parent = parent_with_repositories(pom, repos)
137+
return repos unless parent
138+
139+
repos + gather_repository_urls(pom: parent)
140+
end
141+
142+
sig do
143+
params(
144+
pom: Dependabot::DependencyFile
145+
).returns(
146+
T::Array[T::Hash[Symbol, T.untyped]]
147+
)
148+
end
149+
def repositories_from_pom(pom)
150+
doc = Nokogiri::XML(pom.content)
151+
doc.remove_namespaces!
152+
153+
repository_nodes(doc)
154+
.filter_map { |node| build_repo_entry(node, pom) }
155+
end
156+
157+
sig do
158+
params(
159+
node: Nokogiri::XML::Node,
160+
pom: Dependabot::DependencyFile
161+
).returns(T.nilable(T::Hash[Symbol, T.untyped]))
162+
end
163+
def build_repo_entry(node, pom)
164+
url = node.at_css("url")&.text&.strip.to_s
165+
return if url.empty?
166+
167+
entry = serialize_mvn_repo(node)
168+
169+
return if property_blocked?(entry)
170+
return unless http_url?(entry)
171+
172+
serialize_urls(entry, pom)
173+
end
174+
175+
sig { params(entry: T::Hash[Symbol, T.nilable(String)]).returns(T::Boolean) }
176+
def property_blocked?(entry)
177+
contains_property?(T.must(entry.fetch(:url))) && !evaluate_properties?
178+
end
179+
180+
sig { params(entry: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
181+
def http_url?(entry)
182+
entry.fetch(:url)&.start_with?("http")
183+
end
184+
185+
sig do
186+
params(
187+
pom: Dependabot::DependencyFile,
188+
repos: T::Array[T::Hash[Symbol, T.untyped]]
189+
).returns(T.nilable(Dependabot::DependencyFile))
190+
end
191+
def parent_with_repositories(pom, repos)
192+
urls = repos.map { |r| r[:url] }
193+
parent_pom(pom, urls)
194+
end
195+
196+
# Returns the repository XML nodes that should be considered when resolving artifacts.
197+
#
198+
# Selection rules:
199+
# - Always includes repositories declared at the project level.
200+
# - Repositories declared inside <profiles> are included only activated explicitly
201+
#
202+
# @example With active profile
203+
# <profile>
204+
# <activation><activeByDefault>true</activeByDefault></activation>
205+
# <repositories>...</repositories>
206+
# </profile>
207+
#
208+
sig { params(doc: Nokogiri::XML::Document).returns(T::Array[Nokogiri::XML::Node]) }
209+
def repository_nodes(doc)
210+
doc.css(REPOSITORY_SELECTOR).select do |repo_node|
211+
profile = repo_node.ancestors("profile").first
212+
213+
# Not in a profile => always include
214+
next true unless profile
215+
216+
# In a profile => only include when activeByDefault=true
217+
active_by_default_profile?(profile)
146218
end
219+
end
220+
221+
sig { params(profile: Nokogiri::XML::Element).returns(T::Boolean) }
222+
def active_by_default_profile?(profile)
223+
node = profile.at_xpath("./activation/activeByDefault")
224+
return false unless node
147225

148-
repos_in_pom + gather_repository_urls(pom: parent)
226+
node.text.strip.casecmp?("true")
149227
end
150228

151229
sig { returns(T::Boolean) }

maven/spec/dependabot/maven/file_parser/repositories_finder_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,5 +348,21 @@
348348
end
349349
end
350350
end
351+
352+
context "when there are repository declarations in profiles" do
353+
let(:base_pom_fixture_name) { "custom_repositories_pom_with_profiles.xml" }
354+
355+
it "does not include repositories from profiles that are not activated by default" do
356+
expect(repository_urls).to eq(
357+
%w(
358+
https://repo.jenkins-ci.org/public
359+
https://repo.jenkins-ci.org/incrementals-activated
360+
https://repo.jenkins-ci.org/incrementals-activated-2
361+
https://repo.jenkins-ci.org/another-activated
362+
https://repo.maven.apache.org/maven2
363+
)
364+
)
365+
end
366+
end
351367
end
352368
end
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
This POM demonstrates a project that declares repositories in two places:
4+
5+
1) Top-level <repositories>
6+
- Always active.
7+
- Dependabot should always consider these repositories.
8+
9+
2) Repositories within profiles:
10+
- They are conditionally active depending on profile activation.
11+
- Only profiles that are "active by default" should be considered
12+
-->
13+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
14+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
15+
<modelVersion>4.0.0</modelVersion>
16+
<groupId>demo</groupId>
17+
<artifactId>demo</artifactId>
18+
<version>0-SNAPSHOT</version>
19+
<packaging>pom</packaging>
20+
<repositories>
21+
<repository>
22+
<id>repo.jenkins-ci.org</id>
23+
<url>https://repo.jenkins-ci.org/public/</url>
24+
</repository>
25+
</repositories>
26+
<profiles>
27+
<!--- The repositories in this profile should NOT be considered because the profile is not activated (lack of activation defaults to false)-->
28+
<profile>
29+
<id>consume-incrementals</id>
30+
<repositories>
31+
<repository>
32+
<id>incrementals-not-activated</id>
33+
<url>https://repo.jenkins-ci.org/incrementals-disabled/</url>
34+
</repository>
35+
</repositories>
36+
</profile>
37+
<profile>
38+
<!--- The repositories in this profile should be considered because the profile is activated by default -->
39+
<activation>
40+
<activeByDefault>true</activeByDefault>
41+
</activation>
42+
<id>incrementals-activated</id>
43+
<repositories>
44+
<repository>
45+
<id>incrementals-activated</id>
46+
<url>https://repo.jenkins-ci.org/incrementals-activated/</url>
47+
</repository>
48+
<repository>
49+
<id>incrementals-activated-2</id>
50+
<url>https://repo.jenkins-ci.org/incrementals-activated-2/</url>
51+
</repository>
52+
</repositories>
53+
</profile>
54+
<profile>
55+
<!--- The repositories in this profile should be considered because the profile is activated by default -->
56+
<activation>
57+
<!-- Parsing should be case-insensitive because this is also allowed in Maven -->
58+
<activeByDefault>TRUE</activeByDefault>
59+
</activation>
60+
<id>another-activated</id>
61+
<repositories>
62+
<repository>
63+
<id>another-activated</id>
64+
<url>https://repo.jenkins-ci.org/another-activated/</url>
65+
</repository>
66+
</repositories>
67+
</profile>
68+
<profile>
69+
<!--- The repositories in this profile should NOT be considered because the profile is disabled explicitly -->
70+
<activation>
71+
<activeByDefault>false</activeByDefault>
72+
</activation>
73+
<id>Another profile</id>
74+
<repositories>
75+
<repository>
76+
<id>incrementals</id>
77+
<url>https://repo.jenkins-ci.org/some other repo/</url>
78+
</repository>
79+
</repositories>
80+
</profile>
81+
</profiles>
82+
</project>

0 commit comments

Comments
 (0)