Is there an existing issue for this?
Package ecosystem
Docker — specifically Helm values files discovered through package-ecosystem: docker.
Package manager version
N/A — Docker image updates do not invoke a native package manager.
Language version
N/A for the Docker ecosystem. Ruby 4.0.0 was used to execute the focused source-level reproduction below.
Manifest location and content before the Dependabot update
Path: /values.yaml
api:
image:
repository: registry.example.com/example/api
tag: 1.2.0
worker:
image:
repository: registry.example.com/example/worker
tag: 1.2.0
The two independent image repositories intentionally use the same current tag. This is the relevant shape consumed by the Docker ecosystem's Helm values parser: each parent contains an image mapping with separate repository and tag values.
dependabot.yml content
Representative configuration:
version: 2
updates:
- package-ecosystem: docker
directory: "/"
schedule:
interval: weekly
The defect is in file generation after dependency selection; it does not depend on a special allow, ignore, or grouping rule.
Updated dependency
For this reproduction, only the API image is selected for an update:
registry.example.com/example/api:1.2.0
-> registry.example.com/example/api:1.3.0
The worker image is a different dependency and is not selected for an update.
What you expected to see, versus what you actually saw
Expected:
api:
image:
repository: registry.example.com/example/api
tag: 1.3.0
worker:
image:
repository: registry.example.com/example/worker
tag: 1.2.0
Actual:
api:
image:
repository: registry.example.com/example/api
tag: 1.3.0
worker:
image:
repository: registry.example.com/example/worker
tag: 1.3.0
The unrelated worker image is changed because its old tag happens to equal the old tag of the dependency being updated.
The Docker ecosystem Helm updater constructs a regular expression using only the old tag and then applies gsub to the complete file:
|
def update_helm(file, content) |
|
old_tags = old_helm_tags(file) |
|
return if old_tags.empty? |
|
|
|
modified_content = content |
|
replacement = new_helm_tag(file) |
|
|
|
old_tags.each do |old_tag| |
|
old_tag_regex = /^\s*(?:-\s)?(?:tag|version):\s+["']?#{Regexp.escape(old_tag)}["']?(?=\s|$)/ |
|
modified_content = modified_content&.gsub(old_tag_regex) do |old_img_tag| |
|
old_img_tag.gsub(old_tag.to_s, replacement.to_s) |
|
end |
|
end |
|
modified_content |
The expression is not associated with the image repository or the YAML path that produced the dependency. Consequently, every matching tag: or version: line in the file is replaced. Escaping the old tag prevents regex metacharacters from changing the match semantics, but does not scope the match to an image repository.
This can silently change an unrelated deployment image in a Dependabot-generated pull request.
Native package manager behavior
Not applicable. This behavior occurs while Dependabot generates the updated Helm values content.
Images of the diff or a link to the PR, issue, or logs
None. This report uses only the synthetic reproduction.
Smallest manifest that reproduces the issue
The following focused reproduction executes the matching and replacement logic from the linked update_helm implementation:
puts "Ruby #{RUBY_VERSION}"
values_yaml = [
"api:",
" image:",
" repository: registry.example.com/example/api",
" tag: 1.2.0",
"worker:",
" image:",
" repository: registry.example.com/example/worker",
" tag: 1.2.0"
].join("\n") + "\n"
old_tag = "1.2.0"
new_tag = "1.3.0"
old_tag_regex = /^\s*(?:-\s)?(?:tag|version):\s+["']?#{Regexp.escape(old_tag)}["']?(?=\s|$)/
actual = values_yaml.gsub(old_tag_regex) do |matched_line|
matched_line.gsub(old_tag, new_tag)
end
puts actual.inspect
updated_count = actual.lines.count { |line| line.include?(new_tag) }
puts "updated tag count: #{updated_count}"
raise "reproduction failed" unless updated_count == 2
Executed with Ruby 4.0.0, this produces:
Ruby 4.0.0
"api:\n image:\n repository: registry.example.com/example/api\n tag: 1.3.0\nworker:\n image:\n repository: registry.example.com/example/worker\n tag: 1.3.0\n"
updated tag count: 2
This is a focused source-level reproduction of the updater transformation rather than an end-to-end GitHub-native Dependabot run.
A regression test could use two different repositories with an identical old tag and assert that only the image represented by the selected dependency changes. The replacement likely needs to be scoped to the corresponding repository/YAML mapping or parser-recorded image path, rather than matching tag values across the entire file.
Is there an existing issue for this?
Package ecosystem
Docker — specifically Helm values files discovered through
package-ecosystem: docker.Package manager version
N/A — Docker image updates do not invoke a native package manager.
Language version
N/A for the Docker ecosystem. Ruby 4.0.0 was used to execute the focused source-level reproduction below.
Manifest location and content before the Dependabot update
Path:
/values.yamlThe two independent image repositories intentionally use the same current tag. This is the relevant shape consumed by the Docker ecosystem's Helm values parser: each parent contains an
imagemapping with separaterepositoryandtagvalues.dependabot.yml content
Representative configuration:
The defect is in file generation after dependency selection; it does not depend on a special allow, ignore, or grouping rule.
Updated dependency
For this reproduction, only the API image is selected for an update:
The worker image is a different dependency and is not selected for an update.
What you expected to see, versus what you actually saw
Expected:
Actual:
The unrelated worker image is changed because its old tag happens to equal the old tag of the dependency being updated.
The Docker ecosystem Helm updater constructs a regular expression using only the old tag and then applies
gsubto the complete file:dependabot-core/docker/lib/dependabot/shared/shared_file_updater.rb
Lines 152 to 165 in 4ba6d00
The expression is not associated with the image repository or the YAML path that produced the dependency. Consequently, every matching
tag:orversion:line in the file is replaced. Escaping the old tag prevents regex metacharacters from changing the match semantics, but does not scope the match to an image repository.This can silently change an unrelated deployment image in a Dependabot-generated pull request.
Native package manager behavior
Not applicable. This behavior occurs while Dependabot generates the updated Helm values content.
Images of the diff or a link to the PR, issue, or logs
None. This report uses only the synthetic reproduction.
Smallest manifest that reproduces the issue
The following focused reproduction executes the matching and replacement logic from the linked
update_helmimplementation:Executed with Ruby 4.0.0, this produces:
This is a focused source-level reproduction of the updater transformation rather than an end-to-end GitHub-native Dependabot run.
A regression test could use two different repositories with an identical old tag and assert that only the image represented by the selected dependency changes. The replacement likely needs to be scoped to the corresponding repository/YAML mapping or parser-recorded image path, rather than matching tag values across the entire file.