Skip to content

Commit 89a2b43

Browse files
committed
Maven: Ignore repositories from profiles that are not activated
1 parent c18cade commit 89a2b43

File tree

3 files changed

+198
-15
lines changed

3 files changed

+198
-15
lines changed

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

Lines changed: 94 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,101 @@ 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 when the enclosing
201+
# <profile> has <activation><activeByDefault>true</activeByDefault></activation>.
202+
#
203+
# @example With active profile
204+
# <profile>
205+
# <activation><activeByDefault>true</activeByDefault></activation>
206+
# <repositories>...</repositories>
207+
# </profile>
208+
#
209+
sig { params(doc: Nokogiri::XML::Document).returns(T::Array[Nokogiri::XML::Node]) }
210+
def repository_nodes(doc)
211+
doc.css(REPOSITORY_SELECTOR).select do |repo_node|
212+
profile = repo_node.ancestors("profile").first
213+
214+
# Not in a profile => always include
215+
next true unless profile
216+
217+
# In a profile => only include when activeByDefault=true
218+
active_by_default_profile?(profile)
146219
end
220+
end
221+
222+
sig { params(profile: Nokogiri::XML::Element).returns(T::Boolean) }
223+
def active_by_default_profile?(profile)
224+
node = profile.at_xpath("./activation/activeByDefault")
225+
return false unless node
147226

148-
repos_in_pom + gather_repository_urls(pom: parent)
227+
node.text.strip.casecmp?("true")
149228
end
150229

151230
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: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
In this example:
14+
- Profile "public-default" *is* activated by default (activeByDefault=true),
15+
so Dependabot may query its repositories.
16+
- Profile "incrementals" are *not* activated by default (no activeByDefault=true),
17+
so Dependabot should NOT query its repositories
18+
-->
19+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
<groupId>demo</groupId>
23+
<artifactId>demo</artifactId>
24+
<version>0-SNAPSHOT</version>
25+
<packaging>pom</packaging>
26+
<repositories>
27+
<repository>
28+
<id>repo.jenkins-ci.org</id>
29+
<url>https://repo.jenkins-ci.org/public/</url>
30+
</repository>
31+
</repositories>
32+
<profiles>
33+
<!--- The repositories in this profile should NOT be considered because the profile is not activated (lack of activation defaults to false)-->
34+
<profile>
35+
<id>consume-incrementals</id>
36+
<repositories>
37+
<repository>
38+
<id>incrementals-not-activated</id>
39+
<url>https://repo.jenkins-ci.org/incrementals-disabled/</url>
40+
</repository>
41+
</repositories>
42+
</profile>
43+
<profile>
44+
<!--- The repositories in this profile should be considered because the profile is activated by default -->
45+
<activation>
46+
<activeByDefault>true</activeByDefault>
47+
</activation>
48+
<id>incrementals-activated</id>
49+
<repositories>
50+
<repository>
51+
<id>incrementals-activated</id>
52+
<url>https://repo.jenkins-ci.org/incrementals-activated/</url>
53+
</repository>
54+
<repository>
55+
<id>incrementals-activated-2</id>
56+
<url>https://repo.jenkins-ci.org/incrementals-activated-2/</url>
57+
</repository>
58+
</repositories>
59+
</profile>
60+
<profile>
61+
<!--- The repositories in this profile should be considered because the profile is activated by default -->
62+
<activation>
63+
<!-- Parsing should be case-insensitive because this is also allowed in Maven -->
64+
<activeByDefault>TRUE</activeByDefault>
65+
</activation>
66+
<id>another-activated</id>
67+
<repositories>
68+
<repository>
69+
<id>another-activated</id>
70+
<url>https://repo.jenkins-ci.org/another-activated/</url>
71+
</repository>
72+
</repositories>
73+
</profile>
74+
<profile>
75+
<!--- The repositories in this profile should NOT be considered because the profile is disabled explicitly -->
76+
<activation>
77+
<activeByDefault>false</activeByDefault>
78+
</activation>
79+
<id>Another profile</id>
80+
<repositories>
81+
<repository>
82+
<id>incrementals</id>
83+
<url>https://repo.jenkins-ci.org/some other repo/</url>
84+
</repository>
85+
</repositories>
86+
</profile>
87+
</profiles>
88+
</project>

0 commit comments

Comments
 (0)