Security policy bot fails to pull security scanner images from private registry after upgrade to 19.1.2

Summary

After upgrading a self-managed Helm-chart instance from 18.11 to 19.1.2, security scans via Scan Execution Policies started failing when run by the security policy bot, because the bot can no longer pull the scanner images from the project’s container registry (seems to display a permissions error: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed), even though this worked prior to the upgrade.

Steps to reproduce

  • Self-managed GitLab (Helm chart), Ultimate, on 18.11.x with Kubernetes runners.
  • Project A: application project using Scan Execution Policies to inject security scans (SAST/DS/Secret Detection).
  • Project B: private registry project hosting scanner images.
  • Ensure security policy bot is added to Project A; scheduled scans succeed on 18.11.
  • Upgrade GitLab to 19.1.2.
  • Run the scheduled security scans again.

Example Project and Relevant logs

Ticket: https://gitlab.zendesk.com/agent/tickets/737366 (has log output in ticket)

What is the current bug behavior?

All policy-injected security jobs run by the security policy bot fail.

Registry pull fails with:

pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed

What is the expected correct behavior?

Scan Execution Policy jobs started by the security policy bot should be able to pull the configured scanner images from the private registry project, just as they did on 18.11, without extra manual changes after upgrade.

Output of checks

  • Deployment: Self-Managed, Helm chart
  • GitLab: 19.1.2
  • Runner: 19.1.1, Kubernetes
  • Plan: Ultimate

Duo Analysis:

What changed between 18.11 and 19.0

Analyzing the codebase, the Security Policy Bot's permissions were refactored during 19.0 development into a strict, explicit allowlist. Two commits are responsible:

  1. "Refactor policy bot permissions to YAML format" moved the bot's project-scoped abilities out of scattered hardcoded DeclarativePolicy rules into config/authz/roles/security_policy_bot.yml. (ref: !229277 (merged))
  2. "Restrict policy bot to least privilege permissions" then locked this down further by adding a prevent_all rule that revokes every ability except those explicitly listed in that YAML file. (ref: !233966 (merged))

The bot's current permission list does not include read_container_image or build_read_container_image, which are the abilities used to authenticate the bot against a container registry, including cross-project pulls governed by the CI/CD job-token scope. Before this refactor, the bot inherited broader default abilities (guest-role/job-token-scope based rules) that happened to include registry pull access. After the refactor, prevent_all blocks it outright since it's not on the allowlist.

Code details

The restrictive rule lives in ee/app/policies/ee/project_policy.rb:

`# Security Policy Bot permissions are maintained in YAML:

config/authz/roles/security_policy_bot.yml

rule { security_policy_bot }.policy do enable(*::Authz::Role.get(:security_policy_bot).direct_permissions(:project)) end

rule { security_policy_bot }.prevent_all do except(*::Authz::Role.get(:security_policy_bot).direct_permissions(:project)) end `

And the allowlist itself, in config/authz/roles/security_policy_bot.yml, has no container registry permission:

`raw_permissions:

  • build_download_code
  • create_bot_pipeline
  • create_on_demand_dast_scan
  • create_pipeline
  • create_vulnerability_state_transition
  • read_build
  • read_on_demand_dast_scan
  • read_project
  • update_pipeline `

This is a known, actively-being-patched regression class. GitLab already fixed a sibling bug in this area, closed issue #577916 (closed), where the bot got 403 Forbidden downloading job artifacts (same prevent_all/missing-permission pattern, tracked under the "minimal access" epic issues #594740 (closed), #594741 (closed), #594742). That fix added read_build to the YAML list, but read_container_image/build_read_container_image for cross-project registry pulls were not addressed.

Edited by 🤖 GitLab Bot 🤖