Add pipeline execution policy bot access settings to project_settings

What does this MR do and why?

Feature overview

Scheduled pipeline execution policies allow security teams to enforce scheduled pipelines across projects in their organization. Currently, the CI configuration referenced by these policies must be stored in the security policy project itself. This is limiting because organizations often maintain shared CI config files in dedicated repositories.

This MR is part of a feature that allows CI config project owners to grant security policy bots read access to specific CI/CD configuration files in their project. When a pipeline execution policy references a CI config from an external project, the security policy bot can then fetch it — but only if the CI config project owner has explicitly opted in and the file matches the allowed patterns.

How it works

Three new settings are added to project_settings:

Setting Type Description
pipeline_execution_policy_bot_access_enabled boolean Master toggle — must be true for any bot access
pipeline_execution_policy_bot_access_file_patterns text[] Glob patterns defining which files bots can read (e.g. policy-ci.yml, security/**/*.yml)
pipeline_execution_policy_bot_access_group_id bigint (FK → namespaces) Optional group restriction — only bots from projects within this group hierarchy are allowed. Defaults to the CI config project's root group

When access is checked via #allows_pipeline_execution_policy_bot_access?(file_path:, bot_project:), it verifies:

  1. The feature is enabled on the project setting
  2. An experiment flag is enabled on at least one security policy configuration
  3. The bot's project is within the allowed group hierarchy
  4. The requested file path matches at least one of the allowed glob patterns
  5. The file path contains no path traversal (..) or absolute paths

Access is denied by default — the CI config project owner must explicitly opt in.

What this MR implements

This is part 1 of splitting the PoC MR into smaller reviewable pieces. It includes:

  • 3 database migrations: adding the columns, an index on pipeline_execution_policy_bot_access_group_id, and a foreign key to namespaces
  • Model concern (Security::PipelineExecutionPolicyBotAccess): the access-check logic, validations, and association
  • EE extension to ProjectSetting: includes the concern
  • Specs for the concern

The CI config file access integration (wiring the access check into the pipeline creation flow) will follow in a subsequent MR.

References

How to set up and verify locally

1. Run migrations

bin/rails db:migrate

2. Create test data

Open a Rails console (bin/rails console) and set up the test scenario:

# Find or create a group hierarchy
group = Group.find_or_create_by!(name: 'test-group', path: 'test-group')
subgroup = Group.find_or_create_by!(name: 'test-subgroup', path: 'test-subgroup', parent: group)

# Create a CI config project (the project that stores shared CI config files)
ci_config_project = Project.find_by_full_path('test-group/ci-config-project') ||
  Projects::CreateService.new(User.first, { name: 'ci-config-project', namespace: group }).execute

# Create a target project (where the policy is enforced)
target_project = Project.find_by_full_path('test-group/test-subgroup/target-project') ||
  Projects::CreateService.new(User.first, { name: 'target-project', namespace: subgroup }).execute

# Create a security policy bot user for the target project
bot = User.find_by(username: "gitlab_security_policy_project_#{target_project.id}_bot") ||
  User.create!(
    name: 'Security Policy Bot',
    username: "gitlab_security_policy_project_#{target_project.id}_bot",
    email: "security_bot_#{target_project.id}@noreply.gitlab.com",
    user_type: :security_policy_bot,
    skip_confirmation: true,
    projects_limit: 0
  )
target_project.add_guest(bot) unless target_project.member?(bot)

3. Enable bot access on the CI config project

ci_config_project.project_setting.update!(
  pipeline_execution_policy_bot_access_enabled: true,
  pipeline_execution_policy_bot_access_file_patterns: ['policy-ci.yml', 'security/**/*.yml']
)

4. Test access checks

# Should return true — file matches pattern
ci_config_project.project_setting.allows_pipeline_execution_policy_bot_access?(
  file_path: 'policy-ci.yml',
  bot_project: target_project
)

# Should return true — glob pattern match
ci_config_project.project_setting.allows_pipeline_execution_policy_bot_access?(
  file_path: 'security/scans/sast.yml',
  bot_project: target_project
)

# Should return false — file not in allowed patterns
ci_config_project.project_setting.allows_pipeline_execution_policy_bot_access?(
  file_path: 'other-file.yml',
  bot_project: target_project
)

# Should return false — path traversal rejected
ci_config_project.project_setting.allows_pipeline_execution_policy_bot_access?(
  file_path: '../secret.yml',
  bot_project: target_project
)

5. Test group restriction

# Create a project outside the allowed group
other_group = Group.find_or_create_by!(name: 'other-group', path: 'other-group')
other_project = Project.find_by_full_path('other-group/other-project') ||
  Projects::CreateService.new(User.first, { name: 'other-project', namespace: other_group }).execute

# Should return false — bot's project is not in the CI config project's group hierarchy
ci_config_project.project_setting.allows_pipeline_execution_policy_bot_access?(
  file_path: 'policy-ci.yml',
  bot_project: other_project
)

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Andy Schoenen

Merge request reports

Loading
Loading