Restrict policy bot to least privilege permissions

What does this MR do and why?

Restricts the security policy bot to least privilege permissions using prevent_all with explicit exceptions.

Background

The security policy bot is added as a guest member to projects, but it should only have the specific permissions required for its functionality (running security scans, managing vulnerabilities). Previously, the bot inherited all guest permissions, which violates the principle of least privilege.

This MR was created as an alternative to !232637 (closed), which explored using MINIMAL_ACCESS role for the bot. That approach required significant changes across 21 files because MINIMAL_ACCESS is only valid for group members, not project members.

After discussion with @jayswain and @imand3r, we decided the prevent_all approach is cleaner

How it works

  1. Enable explicit permissions: The bot gets only permissions defined in config/authz/roles/security_policy_bot.yml:

    • build_download_code
    • create_bot_pipeline
    • create_on_demand_dast_scan
    • create_pipeline
    • create_vulnerability_state_transition
    • read_on_demand_dast_scan
  2. Prevent everything else: Using prevent_all with except, we block all other permissions the bot would inherit from guest membership.

  3. Exception for conditional permissions: read_build and _read_public_build are excepted from prevent_all so other policy rules (e.g., public build visibility) can still control when they're granted.

Trade-off

The UI still shows "Guest" role for the bot in the members page, even though it has different permissions.

References

How to set up and validate locally

  1. Create a security policy bot and add as guest to a project:

    bot = User.find_by(user_type: :security_policy_bot)
    project = Project.find(<id>)
    project.add_guest(bot)
  2. Verify permissions are restricted:

    policy = ProjectPolicy.new(bot, project)
    
    # Should be ALLOWED (from YAML role):
    policy.allowed?(:create_pipeline)        # => true
    policy.allowed?(:build_download_code)    # => true
    
    # Should be PREVENTED (guest permissions blocked):
    policy.allowed?(:create_issue)           # => false
    policy.allowed?(:read_wiki)              # => false
    policy.allowed?(:upload_file)            # => false

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