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:
- The feature is enabled on the project setting
- An experiment flag is enabled on at least one security policy configuration
- The bot's project is within the allowed group hierarchy
- The requested file path matches at least one of the allowed glob patterns
- 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 tonamespaces - 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
- Parent issue: #588852 (closed)
- PoC MR (full feature): !219912 (closed)
How to set up and verify locally
1. Run migrations
bin/rails db:migrate2. 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.